ENGINEER BLOG

ENGINEER BLOG

Pythonとジョブ運行ツールを使って、手作業を自動化してみた。

こんにちは。ITサービス本部の松永です。

今回は、プログラミング言語Pythonとジョブ運行ツールA-AUTO50を使って、
手作業を自動で行うRPAツール[ AUTO Operator ]を作ってみましたので、ご紹介します。

0. きっかけ

私の所属するチームでは、お客様が利用するシステムの運用を行っています。

例えば、

  • システムを構成する機器でアラートが発生したときの原因調査、対応
  • お客様からの依頼対応(データ更新、ミドルウェア再起動、など)

など、[ システムを快適に利用いただくため ]の業務がメインです。

たくさんのシステムを限られたメンバーで運用しているため、[ 自動化 ][ 効率化 ]がチームの至上命題です。

・・・ということで、それに向けた第1歩[ AUTO Operator ]を作ってみることとなりました!

1. AUTO Operatorの姿(=今回作りたいもの)

名前は[ CHILED_OPE.py ]
A-AUTO50上で起動するジョブ[ AUTOOPE3 ]をトリガーとし、以下を自動で行います。

  1. お客様からgmailで受信した依頼メールの解析(差出人、件名、本文)
  2. お客様へ受付完了メール送信
  3. オペレーション実行可否判定(Linuxサーバで稼動するhttpdの再起動)
  4. オペレーション実行
  5. お客様へ完了メール送信

-4.にてオペレーションがNGだった場合-

  1. SEエスカレーションメール送信

-3.にて[ 否 ](オペレーション対象外)と判定された場合-

  1. SEエスカレーションメール送信

<処理フロー>
shori-flow

単純なつくりではありますが、実現に向け、頑張っていこうと思います!

2. 環境準備

まずは、AUTO Operatorを作るための環境を整えます。
尚、以下は用意されている前提でお話を進めます。ご了承ください。

  • gmailが利用可能なWindowsパソコン
  • httpdとSSHが稼動するLinuxサーバ
  • WindowsパソコンとLinuxサーバ間のSSH通信

2-1. Python

2-1-1. Pythonのインストール

AUTO Operator[ CHILED_OPE.py ]を開発するために必要です。
以下サイトからダウンロードし、パソコンにインストールします。
https://www.python.org/
※トップページ[ Downloads ]タブにて、[ Downloads Python 3.5 ]ボタンをクリック

2-1-2. paramikoモジュールのインストール

こちらは、[ CHILED_OPE.py ]からLinuxサーバへSSHで接続、コマンド発行する際に必要なモジュールです。
Pythonインタープリタから以下コマンドを発行し、インストールします。

pip install paramiko

これで[ CHILED_OPE.py ]を作るための準備が整いました。
続いて、A-AUTO50の準備を進めます。

2-2. A-AUTO50

2-2-1. A-AUTO50のインストール

A-AUTO50はユニリタ社が提供する無償のジョブ運行ツールです。
同社のサイトからダウンロードし、パソコンにインストールします。
https://a-auto50.unirita.co.jp/downloads/

以上で準備完了です。
これで、AUTO Operatorを作る環境が整いました。
いよいよ開発をはじめます!

3. Pythonによる[ CHILED_OPE.py ]開発

Pythonインタープリタにて、前述の処理フローを実現するためのコードを記述していきます。
以下、コードの一部を紹介します。

<モジュールインポート>

import sys
import imaplib
import email
import smtplib
import paramiko
import base64
from email.header import decode_header, make_header
from email.mime.text import MIMEText      

まず必要なモジュールをインポートします。
Pythonはデフォルトで備わっているモジュールが豊富(=paramikoのように追加でインストールするケースが少ない)なので便利ですね・・!

<変数セット>

UserName = 'xxxx@gmail.com'                                             #お客様からの依頼メールを受信するgmailアカウント
PassName = 'xxxx'                                                  #gmailアカウントのパスワード
mail = imaplib.IMAP4_SSL('imap.gmail.com')                              #imap設定情報
sender = smtplib.SMTP_SSL('smtp.gmail.com')                             #smtp設定情報
keyword = 'httpd再起動'                                                  #オペレーション実行のトリガーとなるキーワード
client = paramiko.SSHClient()                                           #LinuxサーバへのSSH接続情報①
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())            #LinuxサーバへのSSH接続情報②
client.connect('192.xxx.xxx.xxx', username='xxxx', password='xxxx')     #LinuxサーバへのSSH接続情報③
operation = 'service httpd restart'                                     #オペレーション時の実行コマンド

各変数の設定値(=依頼要件)を変えれば、それに応じた自動オペレーションが可能・・!
といいたいのですが、まだ検証が足りてません。。。。
このあと頑張ります・・!

<依頼メールの解析>

msg_subject = email.header.decode_header(msg.get('Subject'))[0][0]
subject = str(msg_subject.decode(msg_encoding))

お客様からgmailで受領した依頼メールの件名を解析する際のコードです。(上述の処理フロー①)
同じ要領で差出人、本文も解析します。

<受付完了メール送信>

message = MIMEText('ご依頼を受け付けました。作業結果は追ってご連絡します。')
message['Subject'] = '【ご依頼受付のお知らせ】Re:'+subject
sender.login(UserName, PassName)
sender.sendmail(UserName, email_from, message.as_string())

お客様へ受付完了メールを送信する際のコードです。(上述の処理フロー②)

<オペレーション>

#お客様からの依頼メール本文に[ httpd再起動 ]が記述されていたら、LinuxサーバにSSH接続し、httpdを再起動。(上述の処理フロー④)
if keyword in body:
        stdin,stdout,stderr = client.exec_command(operation)
        buf = stderr.read()

        #結果判定。失敗したら、SEへエスカレーションメールを送信する。(上述の処理フロー⑥)
        if len(buf) > 0:
            mail.copy(num,'operation-error')
            mail.store(num, '+FLAGS','\\deleted')
            message = MIMEText('オペレーションが失敗しました。対処ならびに完了後の依頼元への報告をお願いします。')
            message['Subject'] = '【緊急】【エスカレーションのお知らせ】Re:'+subject
            sender.login(UserName, PassName)
            sender.sendmail(UserName, email_from, message.as_string())
            sender.quit()
            sys.exit(1)
        
        #成功したら、お客様へ作業完了メールを送信する。(上述の処理フロー⑤)
        else:
            mail.copy(num,'operation-success')
            mail.store(num, '+FLAGS','\\deleted')
            message = MIMEText('ご依頼の作業が完了しましたので、ご報告します。')
            message['Subject'] = '【ご依頼完了のお知らせ】Re:'+subject
            sender.login(UserName, PassName)
            sender.sendmail(UserName, email_from, message.as_string())
            sender.quit()
            sys.exit(0)

#依頼メール本文に[ httpd再起動 ]が記述されてなかったら、SEへエスカレーションメールを送信する。(上述の処理フロー⑦)
else:
    mail.copy(num,'operation-success')
    mail.store(num, '+FLAGS','\\deleted')
    message = MIMEText(body)
    message['Subject'] = '【エスカレーションのお知らせ】'+subject
    sender.login(UserName, PassName)
    sender.sendmail(UserName, email_from, message.as_string())
    sender.quit()
    sys.exit(0)

長い・・・ので、コード内にコメント(#)で説明を記載しました。
コーディングするのに一番苦労しました。。。

4. A-AUTO50による[CHILED_OPE.py]の起動設定

ここでは、前項で開発した[ CHILED_OPE.py ]を起動するための仕組みを開発していきます。

4-1. ジョブ設定

A-AUTO50は、ブラウザ経由で設定や操作・監視が行えます。
こちらでジョブを設定します。名前は[ AUTOOPE3 ]です。

4-2. 起動用バッチファイル作成

A-AUTO50のジョブからは[ CHILED_OPE.py ]を直接起動することができないため、
起動用のバッチファイルを作成します。
名前は [ AUTOOPE3.bat ]です。

@echo off

C:\Users\user\AppData\Local\Programs\Python\Python35-32\python.exe C:\BSP\AUW\INSTALL\PRIMSCRIPT\CHILED_AUTOOPE.py
IF %ERRORLEVEL%==0 GOTO SUCCESS

:ERROR
aexit 1
GOTO DONE

:SUCCESS
aexit 0

:DONE
exit

コードは単純で、[ CHILED_OPE.py ]を起動し、同プログラムから渡される終了コードをジョブに返値するだけです。

これでAUTO Operatorを作成できました。
業務フロー通りに稼動するか、試してみます。

5. テスト

テストは3ケース行います。まずは、オペレーションが成功するか、試してみます。。。

5-1. ケースA:httpd再起動成功

お客様から以下メールを受信しましたので、A-AUTO50でジョブ[ AUTOOPE3 ]を起動します。
(以降、AUTO Operatorがうまく動くかどうか見ていきます!)

irai1

jobstart

↓ ↓ ↓ ↓ ↓

お客様へ依頼受付メールを送信

iraiuketsuke

↓ ↓ ↓ ↓ ↓

httpdの再起動依頼であったため、Linuxサーバへ接続し、httpdを再起動

restart

↓ ↓ ↓ ↓ ↓

お客様へ作業完了メールを送信

sagyokanryo

↓ ↓ ↓ ↓ ↓

A-AUTO50のジョブ[ AUTOOPE3 ]が正常終了

jobend

OKです、うまく稼動しました!
続いて、次のケースを試してみます。

5-2. ケースB:httpd再起動失敗。SEエスカレーション

お客様から以下メールを受信しましたので、ケースAと同様にジョブ[ AUTOOPE3 ]を起動します。
(さあ、うまくいくかどうか・・!?)

irai1

jobstart2

↓ ↓ ↓ ↓ ↓

お客様へ依頼受付メールを送信

iraiuketsuke

↓ ↓ ↓ ↓ ↓

httpdの再起動依頼であったため、Linuxサーバへ接続し、httpdを再起動
・・・・しましたが、失敗。
※今回、[ CHILED_OPE.py ]内のhttpd再起動の記述を意図的に変えて失敗させました。
[service httpd restart ] → [ service httpd restartdddddd]

↓ ↓ ↓ ↓ ↓

SEへエスカレーションメールを送信

se-irai

↓ ↓ ↓ ↓ ↓

A-AUTO50のジョブ[ AUTOOPE3 ]が異常終了

jobng

これもOKです!!想定通り稼動しました!
続いて最後のケースを試してみます。

5-3. ケースC:[ AUTO Operator ]対応不能。SEエスカレーション

これは、httpd再起動以外の依頼が発生した際の対応です。試してみます。

お客様から以下メールを受信しましたので、A-AUTO50でジョブ[ AUTOOPE3 ]を起動します。
(今回は、httpd再起動ではなく、データ更新の依頼です)

dataupdate

jobstart3

↓ ↓ ↓ ↓ ↓

お客様へ依頼受付メールを送信

iraiuketsuke2

↓ ↓ ↓ ↓ ↓

httpdの再起動でないため、SEへエスカレーションメールを送信

se-irai2

OKです!!
3ケース実行し、無事、処理フロー通りの稼動を確認することができました!

6. おわりに

今回、業務自動化に向けた第1歩として、AUTO Operatorを作ってみましたが、
まだまだ、バージョンアップできるな、と感じました。

例えば、

  • 緊急エスカレーションの場合は、SEに電話をかけるようにする。
    (Twilioなどを使用して)
  • 変数の設定値のような可変のものは、Pythonプログラムから外出しすることでプログラムを汎用化する。
    (A-AUTOの[ ノーマル実行時のパラメータ ]機能を使用して)

などなど。

AUTO Operatorが便利になればなるほど、手作業を行っていた人の手を空けられます。
そして、空いた手で、更なる改善を進めていくことができますので、引き続きいろいろとチャレンジしていきたいと思います!