Kompira

Menu Menu

Enterprise Technical information

外部との連携 アラートメールをフィルタリングして転送する

Unix/Linux 系 OS ではシステムのセルフチェックを行い、結果を管理者にメールで通知するといった仕組みが動いています。また、システム管理ツールがサーバ監視を行い、異常時にはアラートメールを送信するように設定しているケースも多くあります。しかし、管理しているサーバー数が多いとメールの数も膨大となり、すべてに目を通すことは現実的ではありません。そのため、受信したメールのうち警告の通知だけを読みたい、もしくは障害通知だけ受け取れればよいといった要望があります。

今回は、受信したメールのうち差出人やタイトルが一定のパターンに一致するものをフィルタリングし、転送するジョブフローを作成します。

 


 

動作確認環境

ソフトウェア バージョン
Kompira Enterprise 1.4.10.post10
OS CentOS 6.10

または

ソフトウェア バージョン
Kompira Enterprise 1.5.5.post7
OS CentOS 7.8.2003

または

ソフトウェア バージョン
Kompira Enterprise 1.6.2.post4
OS CentOS 8.2.2004

 


 

メール転送の設定

ここでは、メールサーバーより IMAP4/POP3 プロトコルを通じて、メールを取り込む「メールチャネル」を利用します。取得の際、メールはサーバーから削除されます。また Kompira Enterprise のメールチャネルがチェックできるアカウントは1つであるため、メールサーバーに Kompira Enterprise 用のメールアカウントを作成し、そのアカウントにメールが転送されるように設定を行ってください。

 

メールチャネルの設定

まず、メール受信を行うためのメールチャネルを作成します。
先程設定した転送先のメールアドレス宛のメールを受信する、”メール受信チャネル” という名前のメールチャネルを作成します。メールチャネルの詳細については、「メール受信をトリガーにしてジョブフローを実行する」にて紹介しております。

 

メールを処理するジョブフローの作成

ここでは
(1) メールアドレスを抽出するジョブフロー「アドレス抽出」
(2) メールを転送するジョブフロー「メール転送」
(3) メールサーバーから受信し、他のジョブフローを呼び出すジョブフロー「フィルタリング転送」
の3つを作成します。

 

(1) メールアドレスを抽出するジョブフロー「アドレス抽出」

メールアドレスの形式としては “taro@example.jp” といったアドレスだけを記載したものと、”コンピラ太郎  <taro@example.jp>” のようにアカウントの名前が併記されているものがあります。アカウント名が併記されている場合には From 行からアドレス以外の項目を除外します。

次のジョブフローを「アドレス抽出」という名前で作成します。

 

| s = "Mail to Kompira<kompira@example.jp>" |
 
[dict = pattern('.*<([^>]+)>', typ="r").match(s)] -> # 正規表現によるマッチ
{ if dict == false |
  then: 
    print(s) -> 
    [addr = s]              # アドレスが単独で記載されたケース
  else: 
    print(dict.groups[0]) -> 
    [addr = dict.groups[0]] # < > 付きでアドレスが記載されたケース
} ->
return(addr)

 

3行目では、正規表現を用いて < > で囲まれた部分をメールアドレスとして取り出すためのパターンを記述しています。パターン型データのメソッド match( ) は、 文字列と正規表現がマッチすればその情報を辞書型にして返し、マッチしなければ false を返します。ここでは < > で囲まれていなかった場合は全文を、囲まれていた場合はその中身を取得しています。

 

(2) メールを転送するジョブフロー「メール転送」

メールの転送は mailto() 関数を使って行います。詳細については「メールを送信する」を参照してください。メールが平文の場合は mailto() 関数でそのまま送信することができますが、メッセージが HTML 形式のものなどは追加の処理が必要になる場合があります。

最初に、受信メールを扱いやすくするために mail_parse() 関数を用いて辞書型データに変換します。本文の内容はキーワード “Body” で取得できます。メールが平文の場合 (Content-Type が “text/plain;” の場合) は

 

{
    'Body': u'disk usage: 100%\r\n',
    'Is_Multipart': False,
    'Content-Type': 'text/plain'
    ...
}

 

のようになりますが、HTML 形式のメッセージと一緒に送られる場合 (Content-Type が “multipart/alternative;” の場合など) は

 

{
    'Body':[
        {
            'Body': u'disk usage: 100%\r\n', 
            'Is-Multipart': False, 
            'Content-Type': 'text/plain'
            ...
        }, 
        {
            'Body':'< div dir="ltr" > disk usage: 100% </div>\r\n', 
            'Is-Multipart': False, 
            'Content-Type': 'text/html' 
            ...
        }
    ],
    'Is-Multipart': True,
    'Content-Type': 'multipart/alternative'
    ...
}

 

のように平文と HTML がそれぞれ配列の要素として格納されます。 このため、Body 要素で渡されたデータが文字列の場合はそのまま処理を行い、配列の場合はそれぞれの要素を抽出して処理を行います。 この処理を行うジョブフロー「メール転送」を次のように作成します。

 

| param_to |      # 転送先メールアドレス
| param_from |    # 差出人メールアドレス
| param_subject | # メールのタイトル
| param_body |    # メールの本文
 
{ if type(param_body) == "String" |
  then: 
    print("Plain Text メールです。") ->
    mailto(
        to=param_to,
        from=param_from,
        subject=param_subject,
        body=param_body
    )
  else:
    print("HTML を含むメールです。" ) ->
    mailto(
        to=param_to,
        from=param_from,
        subject=param_subject, 
        body=param_body[0].Body,
        html_content=param_body[1].Body
    )
}

 

このジョブフローでは type() 関数を用いて、本文の型をチェックしています。平文の場合には “String” という文字列が返りますので、このまま mailto() 関数の body パラメータに渡します。それ以外の場合は配列と判断し、平文部分を body パラメータに、HTML 部分を html_content パラメータに渡します。

 

(3) メールサーバーから受信し、他のジョブフローを呼び出すジョブフロー「フィルタリング転送」

ここでは、作成したメールチャネルとジョブフローを組み合わせ、実際に転送を行うジョブフロー「フィルタリング転送」を作成します。
条件として

1. タイトルに “ALERT” または “WARNING” という文字列を含む
2. 差出人が “loginaccount@system.local” である

の両方を満たす場合にメールを転送するものとします。
メールの差出人は適宜変更してください。
条件を満たさない場合には ”スキップします” というメッセージを表示して次のメールを処理します。

 

| forward_addr = "admin@example.jp" |  # 転送先アドレス

<./メール受信チャネル> ->
[msg_dict = mail_parse($RESULT)] ->
 
# 送信者アドレス抽出
[./アドレス抽出: msg_dict.From] ->
[sender_addr = $RESULT] ->

# タイトル抽出
[subject = msg_dict.get_item("Subject", "")] -> 

# タイトルに指定の文字列が含まれるか確認
[dict = pattern('.*(ALERT|WARNING)', typ="r").match(subject)] ->
 
# タイトルに含まれていて、送信元が一致する場合は転送
{ if dict != false and sender_addr == "loginaccount@system.local" |
  then: 
    [./メール転送: 
        param_to=forward_addr,
        param_from=sender_addr, 
        param_subject=subject,
        param_body=msg_dict.Body
    ]
  else:
    print("条件に一致しないためスキップします。: ", sender_addr, subject)
} ->
self() # ジョブフローの先頭に戻る

 

転送条件として各ヘッダーの項目や本文の内容などを用いることが出来ます。例えば受信日時は msg_dict.Date で参照することができます。本文を処理する際にはジョブフロー「メール転送」で見てきたように、HTML 形式のメッセージの扱いにご注意ください。システムから送信されるメールが対象の場合はほぼ全てが平文のメールであるため、HTML を含むメールを無視しても問題は少ないと思われますが、処理する対象によっては重要となります。

また、本文の構造が複雑な場合には、ジョブフローのパターンマッチングだけでは難しい場合があります。その場合は Kompira Enterprise のライブラリ型オブジェクトを使うことで、Python 言語の文字列処理関数を利用できます。ライブラリ型オブジェクトについては、「Python で記述された処理を Kompira から呼び出す」を参考にしてみてください。

スタートガイド

Kompiraジョブフローの基礎

ジョブフローの簡単な例

Kompiraの機能

外部との連携