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 から呼び出す」を参考にしてみてください。