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 |
または
| ソフトウェア | バージョン |
|---|---|
| Kompira Enterprise | 1.6.8 |
| OS | CentOS Stream 8 |
メール転送の設定
ここでは、メールサーバーより 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 から呼び出す」を参考にしてみてください。








