MonitのXMLをPython3でParseする

はじめに

Monitの監視URLにクエリパラメータを付与するとステータス情報のXMLが取得できます。これを使ってXMLをパースし、プロセス監視の情報を取得してみます。

開発環境

  • Mac Book Pro Late 2018
  • VSCode1.41.1
  • Python3.7.3

サーバ環境

  • CentOS7.6.1810(Core)
  • Monit 5.25.1

目次

  1. monit設定ファイルの編集
  2. monitのソースコードをちょっとだけ覗く
  3. Python3でコードを書く
  4. 実行結果の確認

monit設定ファイルの編集

/etc/monitrcで全体の設定を行い、個別のプロセス監視については、/etc/monit.d/以下に設定ファイルを追加していきます。

/etc/monitrc

変更点のみ記載します。

set httpd port 2812 and
    use address 10.1.254.70  # only accept connection from localhost
    allow localhost        # allow localhost to connect to the server and
    allow 10.1.254.0/24
    allow admin:admin
  • use address にサーバIPアドレスを設定(デフォルト値はlocalhost)
  • allow 10.1.254.0/24でアクセス許可するネットワークを指定
  • allow admin:adminでBasic認証を有効に
    (SSLを有効にしていないので意味ないですが、Python側でBasic認証の検証をしたかったので有効にしています)

/etc/monit.d/

このディレクトリは/etc/monitrcでincludeされているため、設定ファイルを置いてサービス再起動すれば有効になります。

ここに記載している設定ファイルは、XMLをParseするためにテキトーに設定しているので、実際のサービス監視ではそのまま利用しない方が良いです。
以下のファイルを作成します。

# ll /etc/monit.d/
合計 28
-rw-r--r-- 1 root root 329  1月 19 15:02 dovecot
-rw-r--r-- 1 root root 262  1月 19 14:50 httpd
-rw-r--r-- 1 root root  51 12月 27  2017 logging
-rw-r--r-- 1 root root 262  1月 19 14:51 named
-rw-r--r-- 1 root root 325  1月 19 15:01 sendmail
-rw-r--r-- 1 root root 253  1月 19 15:05 sshd
-rw-r--r-- 1 root root  53  1月 22  2020 test

/etc/monit.d/dovecot

110番と143番ポートの監視。いずれかのポート監視が失敗した場合、プロセスを再起動します。再起動に数度失敗した場合に監視設定を外します。

check process dovecot 
    with pidfile "/var/run/dovecot/master.pid"
    start program = "/usr/bin/systemctl start dovecot"
    stop  program = "/usr/bin/systemctl stop dovecot"
    if failed port 110 with timeout 5 seconds then restart
    if failed port 143 with timeout 5 seconds then restart
    if 5 restarts within 10 cycles then unmonitor

/etc/monit.d/httpd

80番ポートの監視。

check process httpd
    with pidfile "/var/run/httpd/httpd.pid"
    start program = "/usr/bin/systemctl start httpd"
    stop  program = "/usr/bin/systemctl stop httpd"
    if failed port 80 with timeout 5 seconds then restart
    if 5 restarts within 10 cycles then unmonitor

/etc/monit.d/named

53番ポートの監視。

check process named
    with pidfile "/var/run/named/named.pid"
    start program = "/usr/bin/systemctl start named"
    stop  program = "/usr/bin/systemctl stop named"
    if failed port 53 with timeout 5 seconds then restart
    if 5 restarts within 10 cycles then unmonitor

/etc/monit.d/sendmail

25番と587番ポートの監視。

check process sendmail 
    with pidfile "/var/run/sendmail.pid"
    start program = "/usr/bin/systemctl start sendmail"
    stop  program = "/usr/bin/systemctl stop sendmail"
    if failed port 25 with timeout 5 seconds then restart
    if failed port 587 with timeout 5 seconds then restart
    if 5 restarts within 10 cycles then unmonitor

/etc/monit.d/sshd

22番ポートの監視。

check process sshd 
    with pidfile "/var/run/sshd.pid"
    start program = "/usr/bin/systemctl start sshd"
    stop  program = "/usr/bin/systemctl stop sshd"
    if failed port 22 with timeout 5 seconds then restart
    if 5 restarts within 10 cycles then unmonitor

/etc/monit.d/test

エラー状態のステータスコードを確認するために、存在しないプロセスを監視対象にしています。

check process test
    with pidfile "/var/run/test.pid"

設定が終わったので、設定ファイルのチェックを実施してサービス再起動します。

# monit -t
Control file syntax OK
# systemctl restart monit
#

HTTPでアクセスしてみます。url: http://{ip_addr}:2812

monit_ss

XMLを取得してみます。url: http://{ip_addr}:2812/_status?format=xml

xml

XMLを見ると、<service type="3">がプロセス監視のようです。

<status>512</status> や、 <status>0</status> を見ると、statusが"0"の場合は正常のようです。この辺りは次の項でソースを少しみていきます。

以下は、取得したXML の一部です。

<?xml version="1.0" encoding="ISO-8859-1"?>
<monit>
  <server>
    <id>31d64b8fbae087f8d5a321a62de93ace</id>
    <incarnation>1579922452</incarnation>
    <version>5.25.1</version>
    <uptime>574</uptime>
    <poll>30</poll>
    <startdelay>0</startdelay>
    <localhostname>localhost.localhost</localhostname>
    <controlfile>/etc/monitrc</controlfile>
    <httpd>
      <address>10.1.254.70</address>
      <port>2812</port>
      <ssl>0</ssl>
    </httpd>
  </server>
  <platform>
    <name>Linux</name>
    <release>3.10.0-957.1.3.el7.x86_64</release>
    <version>#1 SMP Thu Nov 29 14:49:43 UTC 2018</version>
    <machine>x86_64</machine>
    <cpu>2</cpu>
    <memory>3880732</memory>
    <swap>2097148</swap>
  </platform>
  <service type="3">
    <name>test</name>
    <collected_sec>1579612105</collected_sec>
    <collected_usec>444154</collected_usec>
    <status>512</status>
    <status_hint>0</status_hint>
    <monitor>1</monitor>
    <monitormode>0</monitormode>
    <onreboot>0</onreboot>
    <pendingaction>0</pendingaction>
  </service>
  <service type="3">
    <name>sshd</name>
    <collected_sec>1579612105</collected_sec>
    <collected_usec>444302</collected_usec>
    <status>0</status>
    <status_hint>0</status_hint>
    <monitor>1</monitor>
    <monitormode>0</monitormode>
    <onreboot>0</onreboot>
    <pendingaction>0</pendingaction>
    <pid>5948</pid>
    <ppid>1</ppid>
    <uid>0</uid>
    <euid>0</euid>
    <gid>0</gid>
    <uptime>574</uptime>
    <threads>1</threads>
    <children>2</children>
    <memory>
      <percent>0.1</percent>
      <percenttotal>0.3</percenttotal>
      <kilobyte>4360</kilobyte>
      <kilobytetotal>12728</kilobytetotal>
    </memory>
    <cpu>
      <percent>0.0</percent>
      <percenttotal>0.0</percenttotal>
    </cpu>
    <read>
      <bytes>
        <count>0</count>
        <total>1708032</total>
      </bytes>
    </read>
    <write>
      <bytes>
        <count>0</count>
        <total>364544</total>
      </bytes>
    </write>
    <port>
      <hostname>localhost</hostname>
      <portnumber>22</portnumber>
      <request><![CDATA[]]></request>
      <protocol>DEFAULT</protocol>
      <type>TCP</type>
      <responsetime>0.000139</responsetime>
    </port>
  </service>
  <service type="5">
    <name>localhost.localhost</name>
    <collected_sec>1579612105</collected_sec>
    <collected_usec>445177</collected_usec>
    <status>0</status>
    <status_hint>0</status_hint>
    <monitor>1</monitor>
    <monitormode>0</monitormode>
    <onreboot>0</onreboot>
    <pendingaction>0</pendingaction>
    <system>
      <load>
        <avg01>0.00</avg01>
        <avg05>0.04</avg05>
        <avg15>0.05</avg15>
      </load>
      <cpu>
        <user>0.0</user>
        <system>0.1</system>
        <wait>0.0</wait>
      </cpu>
      <memory>
        <percent>13.1</percent>
        <kilobyte>508088</kilobyte>
      </memory>
      <swap>
        <percent>0.0</percent>
        <kilobyte>0</kilobyte>
      </swap>
    </system>
  </service>
</monit>

back

monitのソースコードをちょっとだけ覗く

https://mmonit.com/monit/dist/からmonit-5.25.1.tar.gzを取得します。

ここでは、プロセス監視のParseに必要な項目として、以下の項目をみていきます。

  • Service Type
  • Monitorステータスコード
  • Statusコード
  • Status文言

Service Type

まずService Typeの定義から確認していきます。Service Typeとは何かということですが、https://mmonit.com/monit/documentation/monit.html#Service-checksで規定されている9つのサービスタイプを示しています。

monit-5.25.1/src/monit.h

typedef enum {
        Service_Filesystem = 0,
        Service_Directory,
        Service_File,
        Service_Process,
        Service_Host,
        Service_System,
        Service_Fifo,
        Service_Program,
        Service_Net,
        Service_Last = Service_Net
} __attribute__((__packed__)) Service_Type;

取得したいService Typeはここで確認できます。

今回着目しているプロセス監視についてのService TypeはService_Processなので、"3"となります。XMLをParseする際にXPathを使用してノードを取得するので、その時に使用する値となります。

Monitorステータスコード

監視対象となっているかどうかを示すものです。監視対象となっていない場合、Monitの監視画面のStatus欄にNot monitoredと表示されます。

monit-5.25.1/src/monit.h

typedef enum {
        Monitor_Not     = 0x0,
        Monitor_Yes     = 0x1,
        Monitor_Init    = 0x2,
        Monitor_Waiting = 0x4
} __attribute__((__packed__)) Monitor_State;

HTML生成を行っている以下のソースでステータスに対応する文言がハードコーディングされていました。

monit-5.25.1/src/http/cervlet.c

static char *get_monitoring_status(Output_Type type, Service_T s, char *buf, int buflen) {
        ASSERT(s);
        ASSERT(buf);
        if (s->monitor == Monitor_Not) {
                if (type == HTML)
                        snprintf(buf, buflen, "Not monitored");
                else
                        snprintf(buf, buflen, Color_lightYellow("Not monitored"));
        } else if (s->monitor & Monitor_Waiting) {
                if (type == HTML)
                        snprintf(buf, buflen, "Waiting");
                else
                        snprintf(buf, buflen, Color_white("Waiting"));
        } else if (s->monitor & Monitor_Init) {
                if (type == HTML)
                        snprintf(buf, buflen, "Initializing");
                else
                        snprintf(buf, buflen, Color_lightBlue("Initializing"));
        } else if (s->monitor & Monitor_Yes) {
                if (type == HTML)
                        snprintf(buf, buflen, "Monitored");
                else
                        snprintf(buf, buflen, "Monitored");
        }
        return buf;
}

因みに、if (type == HTML)else では、monitのstatusサブコマンドの表示を生成しているようです。

# monit status sendmail
Monit 5.25.1 uptime: 15m

Process 'sendmail'
  status                       Not monitored
  monitoring status            Not monitored
  monitoring mode              active
  on reboot                    start
  data collected               Wed, 22 Jan 2020 00:44:06

Statusコード

Statusコードは、Event_Typeとして定義されています。XML 内の<status></status> 要素の値として使用されます。

monit-5.25.1/src/event.h

typedef enum {
        Event_Null       = 0x0,
        Event_Checksum   = 0x1,
        Event_Resource   = 0x2, //FIXME: split to more specific events (cpu, totalcpu, mem, totalmem, loadaverage, space, inode, ...)
        Event_Timeout    = 0x4,
        Event_Timestamp  = 0x8, //FIXME: split to more specific events (atime, mtime, ctime)
        Event_Size       = 0x10,
        Event_Connection = 0x20,
        Event_Permission = 0x40,
        Event_Uid        = 0x80,
        Event_Gid        = 0x100,
        Event_NonExist   = 0x200,
        Event_Invalid    = 0x400,
        Event_Data       = 0x800,
        Event_Exec       = 0x1000,
        Event_FsFlag     = 0x2000,
        Event_Icmp       = 0x4000,
        Event_Content    = 0x8000,
        Event_Instance   = 0x10000,
        Event_Action     = 0x20000,
        Event_Pid        = 0x40000,
        Event_PPid       = 0x80000,
        Event_Heartbeat  = 0x100000,
        Event_Status     = 0x200000,
        Event_Uptime     = 0x400000,
        Event_Link       = 0x800000, //FIXME: split to more specific events (link status, link errors)
        Event_Speed      = 0x1000000,
        Event_Saturation = 0x2000000,
        Event_ByteIn     = 0x4000000,
        Event_ByteOut    = 0x8000000,
        Event_PacketIn   = 0x10000000,
        Event_PacketOut  = 0x20000000,
        Event_Exist      = 0x40000000,
        Event_All        = 0x7FFFFFFF
} Event_Type;

Status文言

XMLをParseしてStatusコードを取得してもどういった意味かわからないため、対応する文言テーブルを確認します。EventTable_T型の配列になっており、1番目の要素がMonitの監視画面のStatus欄に表示される文言となります。Statusコードとこの変換テーブルをPythonに移植します。

monit-5.25.1/src/event.c

EventTable_T Event_Table[] = {
        {Event_Action,     "Action done",               "Action done",                "Action done",              "Action done"},
        {Event_ByteIn,     "Download bytes exceeded",   "Download bytes ok",          "Download bytes changed",   "Download bytes not changed"},
        {Event_ByteOut,    "Upload bytes exceeded",     "Upload bytes ok",            "Upload bytes changed",     "Upload bytes not changed"},
        {Event_Checksum,   "Checksum failed",           "Checksum succeeded",         "Checksum changed",         "Checksum not changed"},
        {Event_Connection, "Connection failed",         "Connection succeeded",       "Connection changed",       "Connection not changed"},
        {Event_Content,    "Content failed",            "Content succeeded",          "Content match",            "Content doesn't match"},
        {Event_Data,       "Data access error",         "Data access succeeded",      "Data access changed",      "Data access not changed"},
        {Event_Exec,       "Execution failed",          "Execution succeeded",        "Execution changed",        "Execution not changed"},
        {Event_FsFlag,     "Filesystem flags failed",   "Filesystem flags succeeded", "Filesystem flags changed", "Filesystem flags not changed"},
        {Event_Gid,        "GID failed",                "GID succeeded",              "GID changed",              "GID not changed"},
        {Event_Heartbeat,  "Heartbeat failed",          "Heartbeat succeeded",        "Heartbeat changed",        "Heartbeat not changed"},
        {Event_Icmp,       "ICMP failed",               "ICMP succeeded",             "ICMP changed",             "ICMP not changed"},
        {Event_Instance,   "Monit instance failed",     "Monit instance succeeded",   "Monit instance changed",   "Monit instance not changed"},
        {Event_Invalid,    "Invalid type",              "Type succeeded",             "Type changed",             "Type not changed"},
        {Event_Link,       "Link down",                 "Link up",                    "Link changed",             "Link not changed"},
        {Event_NonExist,   "Does not exist",            "Exists",                     "Existence changed",        "Existence not changed"},
        {Event_PacketIn,   "Download packets exceeded", "Download packets ok",        "Download packets changed", "Download packets not changed"},
        {Event_PacketOut,  "Upload packets exceeded",   "Upload packets ok",          "Upload packets changed",   "Upload packets not changed"},
        {Event_Permission, "Permission failed",         "Permission succeeded",       "Permission changed",       "Permission not changed"},
        {Event_Pid,        "PID failed",                "PID succeeded",              "PID changed",              "PID not changed"},
        {Event_PPid,       "PPID failed",               "PPID succeeded",             "PPID changed",             "PPID not changed"},
        {Event_Resource,   "Resource limit matched",    "Resource limit succeeded",   "Resource limit changed",   "Resource limit not changed"},
        {Event_Saturation, "Saturation exceeded",       "Saturation ok",              "Saturation changed",       "Saturation not changed"},
        {Event_Size,       "Size failed",               "Size succeeded",             "Size changed",             "Size not changed"},
        {Event_Speed,      "Speed failed",              "Speed ok",                   "Speed changed",            "Speed not changed"},
        {Event_Status,     "Status failed",             "Status succeeded",           "Status changed",           "Status not changed"},
        {Event_Timeout,    "Timeout",                   "Timeout recovery",           "Timeout changed",          "Timeout not changed"},
        {Event_Timestamp,  "Timestamp failed",          "Timestamp succeeded",        "Timestamp changed",        "Timestamp not changed"},
        {Event_Uid,        "UID failed",                "UID succeeded",              "UID changed",              "UID not changed"},
        {Event_Uptime,     "Uptime failed",             "Uptime succeeded",           "Uptime changed",           "Uptime not changed"},
        {Event_Exist,      "Does exist",                "Exists not",                 "Existence changed",        "Existence not changed"},
        /* Virtual events */
        {Event_Null,       "No Event",                  "No Event",                   "No Event",                 "No Event"}
};

back

Python3でコードを書く

monitのソースコードから流用したコードは、event.pyに全てまとめました。

列挙型で定義したEvent_Type、Service_Typeは、ほぼそのまま流用していますが、Event_Tableは辞書型で定義しました。(※Service_Type のEnumは、結局使用しませんでした。)

event.py

from enum import Enum

class Event_Type(Enum):
    Event_Null       = 0x0
    Event_Checksum   = 0x1
    Event_Resource   = 0x2 #FIXME: split to more specific events (cpu, totalcpu, mem, totalmem, loadaverage, space, inode, ...)
    Event_Timeout    = 0x4
    Event_Timestamp  = 0x8 #FIXME: split to more specific events (atime, mtime, ctime)
    Event_Size       = 0x10
    Event_Connection = 0x20
    Event_Permission = 0x40
    Event_Uid        = 0x80
    Event_Gid        = 0x100
    Event_NonExist   = 0x200
    Event_Invalid    = 0x400
    Event_Data       = 0x800
    Event_Exec       = 0x1000
    Event_FsFlag     = 0x2000
    Event_Icmp       = 0x4000
    Event_Content    = 0x8000
    Event_Instance   = 0x10000
    Event_Action     = 0x20000
    Event_Pid        = 0x40000
    Event_PPid       = 0x80000
    Event_Heartbeat  = 0x100000
    Event_Status     = 0x200000
    Event_Uptime     = 0x400000
    Event_Link       = 0x800000 #FIXME: split to more specific events (link status, link errors)
    Event_Speed      = 0x1000000
    Event_Saturation = 0x2000000
    Event_ByteIn     = 0x4000000
    Event_ByteOut    = 0x8000000
    Event_PacketIn   = 0x10000000
    Event_PacketOut  = 0x20000000
    Event_Exist      = 0x40000000
    Event_All        = 0x7FFFFFFF


Event_Table = {
    Event_Type.Event_Action:     ["Action done",               "Action done",                "Action done",              "Action done"],                  
    Event_Type.Event_ByteIn:     ["Download bytes exceeded",   "Download bytes ok",          "Download bytes changed",   "Download bytes not changed"],   
    Event_Type.Event_ByteOut:    ["Upload bytes exceeded",     "Upload bytes ok",            "Upload bytes changed",     "Upload bytes not changed"],     
    Event_Type.Event_Checksum:   ["Checksum failed",           "Checksum succeeded",         "Checksum changed",         "Checksum not changed"],         
    Event_Type.Event_Connection: ["Connection failed",         "Connection succeeded",       "Connection changed",       "Connection not changed"],       
    Event_Type.Event_Content:    ["Content failed",            "Content succeeded",          "Content match",            "Content doesn't match"],        
    Event_Type.Event_Data:       ["Data access error",         "Data access succeeded",      "Data access changed",      "Data access not changed"],      
    Event_Type.Event_Exec:       ["Execution failed",          "Execution succeeded",        "Execution changed",        "Execution not changed"],        
    Event_Type.Event_FsFlag:     ["Filesystem flags failed",   "Filesystem flags succeeded", "Filesystem flags changed", "Filesystem flags not changed"], 
    Event_Type.Event_Gid:        ["GID failed",                "GID succeeded",              "GID changed",              "GID not changed"],              
    Event_Type.Event_Heartbeat:  ["Heartbeat failed",          "Heartbeat succeeded",        "Heartbeat changed",        "Heartbeat not changed"],    
    Event_Type.Event_Icmp:       ["ICMP failed",               "ICMP succeeded",             "ICMP changed",             "ICMP not changed"],         
    Event_Type.Event_Instance:   ["Monit instance failed",     "Monit instance succeeded",   "Monit instance changed",   "Monit instance not changed"],   
    Event_Type.Event_Invalid:    ["Invalid type",              "Type succeeded",             "Type changed",             "Type not changed"],         
    Event_Type.Event_Link:       ["Link down",                 "Link up",                    "Link changed",             "Link not changed"],         
    Event_Type.Event_NonExist:   ["Does not exist",            "Exists",                     "Existence changed",        "Existence not changed"],    
    Event_Type.Event_PacketIn:   ["Download packets exceeded", "Download packets ok",        "Download packets changed", "Download packets not changed"], 
    Event_Type.Event_PacketOut:  ["Upload packets exceeded",   "Upload packets ok",          "Upload packets changed",   "Upload packets not changed"],   
    Event_Type.Event_Permission: ["Permission failed",         "Permission succeeded",       "Permission changed",       "Permission not changed"],       
    Event_Type.Event_Pid:        ["PID failed",                "PID succeeded",              "PID changed",              "PID not changed"],          
    Event_Type.Event_PPid:       ["PPID failed",               "PPID succeeded",             "PPID changed",             "PPID not changed"],         
    Event_Type.Event_Resource:   ["Resource limit matched",    "Resource limit succeeded",   "Resource limit changed",   "Resource limit not changed"],   
    Event_Type.Event_Saturation: ["Saturation exceeded",       "Saturation ok",              "Saturation changed",       "Saturation not changed"],       
    Event_Type.Event_Size:       ["Size failed",               "Size succeeded",             "Size changed",             "Size not changed"],         
    Event_Type.Event_Speed:      ["Speed failed",              "Speed ok",                   "Speed changed",            "Speed not changed"],       
    Event_Type.Event_Status:     ["Status failed",             "Status succeeded",           "Status changed",           "Status not changed"],       
    Event_Type.Event_Timeout:    ["Timeout",                   "Timeout recovery",           "Timeout changed",          "Timeout not changed"],      
    Event_Type.Event_Timestamp:  ["Timestamp failed",          "Timestamp succeeded",        "Timestamp changed",        "Timestamp not changed"],    
    Event_Type.Event_Uid:        ["UID failed",                "UID succeeded",              "UID changed",              "UID not changed"],          
    Event_Type.Event_Uptime:     ["Uptime failed",             "Uptime succeeded",           "Uptime changed",           "Uptime not changed"],       
    Event_Type.Event_Exist:      ["Does exist",                "Exists not",                 "Existence changed",        "Existence not changed"],    
    #/* Virtual events */
    Event_Type.Event_Null:       ["No Event",                  "No Event",                   "No Event",                 "No Event"] 
}

class Service_Type(Enum): 
    Service_Filesystem = "0"
    Service_Directory = "1"
    Service_File = "2"
    Service_Process = "3"
    Service_Host = "4"
    Service_System = "5"
    Service_Fifo = "6"
    Service_Program = "7"
    Service_Net = "8"


if __name__ == "__main__":
    print(Event_Table[Event_Type(512)][0])

monit.py

HTTPでBasic認証、XML取得、プロセス監視項目の取得、エラー時に結果を出力します。

import xml.etree.ElementTree as ET
import urllib.request
from pathlib import Path

from event import Event_Type
from event import Event_Table
from event import Service_Type #このコードでは未使用

encode = 'utf-8'

def parse_xml(xmlStr):
    root = ET.fromstring(xmlStr)
    print("{:15}{:15}{}".format("process", "monitor", "status"))
    for ps_node in root.findall(".//service[@type='3']"):
        # XPathで、タイプ3(Process)のみを取得対象とする 
        # 取得項目は、name, monitor, staus要素のみ
        process = ps_node.find('name').text
        monitor = ps_node.find('monitor').text
        monitor_desc = 'Monitored' if monitor == '1' else 'Unmonitored'
        status  = ps_node.find('status').text
        status_desc = Event_Table[Event_Type(int(status))][0] 

        if status != '0' or monitor == '0':
            # プロセス異常の場合、statusは"0"以外が設定されている
            # 監視が無効の場合、monitorは"0"に設定されている
            # 正常のものは出力したくない
            print("{:15}{:15}{}".format(process, monitor_desc, status_desc))


def setup_auth(uri, auth_type, user, passwd):
    """Basic、Digest認証がある場合認証を実施"""
    passwd_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
    passwd_mgr.add_password(
            realm=None,
            uri=uri,
            user=user,
            passwd=passwd)
    auth_handler = None
    if auth_type is None:
        return
    elif auth_type == 'basic':
        auth_handler = urllib.request.HTTPBasicAuthHandler(passwd_mgr)
    elif auth_type == 'digest':
        auth_handler = urllib.request.HTTPDigestAuthHandler(passwd_mgr) 
    opener = urllib.request.build_opener(auth_handler)
    urllib.request.install_opener(opener)


def httpRequest(uri, auth_type, user, passwd):
    setup_auth(uri, auth_type, user, passwd)
    req = urllib.request.Request(uri)
    res = urllib.request.urlopen(req)
    return res.read().decode(encode)


def test_main():
    """ test main process """
    uri = "http://10.1.254.70:2812/_status?format=xml"
    auth_type = "basic"
    user = "admin"
    passwd = "admin"

    try:
        result = httpRequest(uri, auth_type, user, passwd)
    except urllib.error.HTTPError as e:
        print("[ERR] Access Error: [" + e.reason + "]" + uri)
    except urllib.error.URLError as ex:
        print("[ERR] Protocol Error: [" + ex.reason + "]" + uri)
    else:
        parse_xml(result)


if __name__ == "__main__":
    test_main()

back

実行結果の確認

MACから実行してみます。

$ python3 monit.py 
process        monitor        status
test           Monitored      Does not exist
sendmail       Unmonitored    No Event

この状態のmonitの監視画面は以下の通り。

monit result

Statusが"Does not exist"のものと、"Not monitored"のもののみが抽出されていることが確認できました。

結果をsmtplibでメール送信するといいかも。Monitにはアラートメールを送信する機能が元々ありますが、複数台のサーバ監視をする場合、大量のメールが送付されてしまうことがあり、鬱陶しいです。まとめてメール送信された方が嬉しいケースがあるのでこのような機能を作ってみました。

複数台をチェックする場合、uriとかuser/passwdはjsonとかで外部定義した方がいいですね。

back