Pythonでサーバ接続ツールを作る(個人的備忘録)

#・前提条件
著者のプログラミング知識は皆無です。
普段からプログラミング言語を触れている人からしたら
イライラする部分が多々あると思いますので、
それを踏まえたうえで以下を読み進めてください。
###作成環境
OS:Microsoft Windows 10 Home
言語:Python3.8

#・作るに至るまでの経緯
MW構築をする上でサーバに接続する際Linuxはマクロツールがあるので特段問題はないが、
WindowsServerを構築する際はリモートデスクトップ接続で直接IPアドレス・ユーザ・パスワードを入力し接続をしていた。
基本的にWindowsServerの要件の方が全体数的に多く、
一々入力しながらOSバージョンも確認するのがしんどいのでぱっと見でわかって接続も出来るものが欲しかった。

また、後々別の現場で利用できる様固有の情報を一切持たせないものを作りたい。

#・ツールでやりたいこと
①　各サーバの情報はリストファイルとして別ファイル管理とする。(ManagerサーバとゲストOSサーバで2ファイル管理)
②　Managerサーバ側は複数の拠点やDCが存在するものとし、サーバ情報以外のものを選択した場合はエラーを出力させる。
③　前提条件として対象サーバは外部ネットワークと繋がっていないものとし、サーバ情報(IP等)の暗号化は意識しない。

#・コードの全体
<details><summary>メインメニュー(コードが長い為折り畳み)</summary><div>

```Python:MainMenu.py
import tkinter as tk
import tkinter.ttk as ttk
import os
import subprocess

# == リストファイル読み込み == #
path = os.path.dirname(__file__)
mng_host_list = open(path + "/mng_host.list","r")
gst_host_list = open(path + "/gst_host.list","r")
mng_lines = mng_host_list.readlines()
gst_lines = gst_host_list.readlines()

# == メインウィンドウ作成 == #
MainMenu = tk.Tk()
MainMenu.title("接続ツール")
MainMenu.geometry("820x580")

# == 接続ボタン押下後処理 == #
def ServerConnect(event):
    ######処理順序は下記の通り。(結果をプロンプト部へ出力させる)
    ###1 選択対象のOS種別をチェックする(WindowsとLinuxが入り混じってた場合はエラー出力で処理終了)
    ###2 選択した対象サーバに対しping発行し死活確認
    ###3 対象サーバへの接続処理
    Text_Prompt.see("end")

def Cancel_Button(event):
    MainMenu.destroy()

# == タブ・プロンプト配置用ウィンドウ作成 == #
Text_Prompt = tk.Text(MainMenu, height = 18, width = 115)
Note_Tab = ttk.Notebook(MainMenu, height = 234, width = 810)

# == ボタン作成 == #
Button_Connect = tk.Button(MainMenu, text = "接続", width = 10)
Button_Connect.bind("<Button-1>", ServerConnect)
Button_Cancel = tk.Button(MainMenu, text = "閉じる", width = 10)
Button_Cancel.bind("<Button-1>", Cancel_Button)

# == タブ作成 ManagerWindowsServer == #
mng_Win_tab = tk.Frame(Note_Tab)
Note_Tab.add(mng_Win_tab, text = "ManagerWindowsServer", padding = 3)

# == タブ作成 ManagerLinuxServer == #
mng_lin_tab = tk.Frame(Note_Tab)
Note_Tab.add(mng_lin_tab, text = "ManagerLinux", padding = 3)

# == タブ作成 ゲストWindowsServer == #
gst_win_tab = tk.Frame(Note_Tab)
Note_Tab.add(gst_win_tab, text = "ゲストWindows", padding = 3)

# == タブ作成 ゲストLinuxServer == #
gst_lin_tab = tk.Frame(Note_Tab)
Note_Tab.add(gst_lin_tab, text = "ゲストLinux", padding = 3)

# == 行作成 == #
# == ManagerWindowsServer == #
tree_mng_win = ttk.Treeview(mng_Win_tab, selectmode = tk.BROWSE)
# == ManagerLinuxServer == #
tree_mng_lin = ttk.Treeview(mng_lin_tab)
# == ゲストWindowsServer == #
tree_gst_win = ttk.Treeview(gst_win_tab, selectmode = tk.BROWSE)
# == ゲストLinuxServer == #
tree_gst_lin = ttk.Treeview(gst_lin_tab)

# == 列作成 == #
# == ManagerWindowsServer == #
tree_mng_win["column"] = (1,2,3,4)
tree_mng_win["show"] = "headings"
# == ManagerLinuxServer == #
tree_mng_lin["column"] = (1,2,3,4)
tree_mng_lin["show"] = "headings"
# == ゲストWindowsServer == #
tree_gst_win["column"] = (1,2,3,4)
tree_gst_win["show"] = "headings"
# == ゲストLinuxServer == #
tree_gst_lin["column"] = (1,2,3,4)
tree_gst_lin["show"] = "headings"

# == ヘッダテキスト == #
# == ManagerWindowsServer == #
tree_mng_win.heading(1, text = "サーバ和名")
tree_mng_win.heading(2, text = "ホスト名")
tree_mng_win.heading(3, text = "IPアドレス")
tree_mng_win.heading(4, text = "サーバ種別")
# == ManagerLinuxServer == #
tree_mng_lin.heading(1, text = "サーバ和名")
tree_mng_lin.heading(2, text = "ホスト名")
tree_mng_lin.heading(3, text = "IPアドレス")
tree_mng_lin.heading(4, text = "サーバ種別")
# == ゲストWindowsServer == #
tree_gst_win.heading(1, text = "サーバ和名")
tree_gst_win.heading(2, text = "ホスト名")
tree_gst_win.heading(3, text = "IPアドレス")
tree_gst_win.heading(4, text = "サーバ種別")
# == ゲストLinuxServer == #
tree_gst_lin.heading(1, text = "サーバ和名")
tree_gst_lin.heading(2, text = "ホスト名")
tree_gst_lin.heading(3, text = "IPアドレス")
tree_gst_lin.heading(4, text = "サーバ種別")

# == 各種タブへの項目追加 == #
# == Managerサーバ == #
for mng_line in mng_lines:
    result_mng = mng_line[:-1].split("\t")
    if 1 == len(result_mng):
        tree_mng_win.insert("", "end", values = (result_mng[0]))
        tree_mng_lin.insert("", "end", values = (result_mng[0]))
    elif "RHEL" in mng_line:
        tree_mng_lin.insert("", "end", values = (result_mng[0],result_mng[1],result_mng[2],result_mng[3]))
    elif "Win" in mng_line:
        tree_mng_win.insert("", "end", values = (result_mng[0],result_mng[1],result_mng[2],result_mng[3]))
    else:
        ###何かしらのエラーが出力された場合はプロンプト部分へエラー出力する
        print("mng_Error!!!")
        print(result_mng)
mng_host_list.close()

# == ゲスト系サーバ == #
for gst_line in gst_lines:
    result_gst = gst_line[:-1].split("\t")
    if "RHEL" in gst_line:
        tree_gst_lin.insert("", "end", values = (result_gst[0],result_gst[1],result_gst[2],result_gst[3]))
    elif "Win" in gst_line:
        tree_gst_win.insert("", "end", values = (result_gst[0],result_gst[1],result_gst[2],result_gst[3]))
    else:
        ###何かしらのエラーが出力された場合はプロンプト部分へエラー出力する
        print("Guels_Error!!!")
        print(result_gst)
gst_host_list.close()

# == パーツ配置 == #
# == ManagerWindowsServer == #
tree_mng_win.grid()
# == ManagerLinuxServer == #
tree_mng_lin.grid()
# == ゲストWindowsServer == #
tree_gst_win.grid()
# == ゲストLinuxServer == #
tree_gst_lin.grid()
# == タブエリア・プロンプトエリア == #
Note_Tab.grid(sticky = tk.N, padx = 3, pady = 3)
Text_Prompt.grid(row = 1, padx = 3, pady = 3)
# == ボタン == #
Button_Cancel.place(relx = 0.6, rely = 0.9)
Button_Connect.place(relx = 0.3, rely = 0.9)
MainMenu.mainloop()
```
</div></details>
<details><summary>読み込むリストファイル(Managerサーバ側)</summary><div>

```list:mng_host.list
########## 東北DC ##########
ServerName001	touhoku001	10.0.0.1	RHEL7.4	root	rootPass
ServerName002	touhoku002	10.0.0.2	RHEL7.2	root	rootPass
ServerName003	touhoku003	10.0.0.3	RHEL6.5	root	rootPass
ServerName004	touhoku004	10.0.0.4	RHEL5.5	root	rootPass
ServerName005	touhoku005	10.0.0.5	WinServer2008	Administrator	adminPass
ServerName006	touhoku006	10.0.0.6	WinServer2012	Administrator	adminPass
ServerName007	touhoku007	10.0.0.7	WinServer2016	Administrator	adminPass
########## 関東DC ##########
ServerName008	kantou001	10.0.0.8	RHEL7.4	root	rootPass
ServerName009	kantou002	10.0.0.9	RHEL7.2	root	rootPass
ServerName010	kantou003	10.0.0.10	RHEL6.5	root	rootPass
ServerName011	kantou004	10.0.0.11	RHEL5.5	root	rootPass
ServerName012	kantou005	10.0.0.12	WinServer2008	Administrator	adminPass
ServerName013	kantou006	10.0.0.13	WinServer2012	Administrator	adminPass
ServerName014	kantou007	10.0.0.14	WinServer2016	Administrator	adminPass
########## 関西DC ##########
ServerName008	kansai001	10.0.0.14	RHEL7.4	root	rootPass
ServerName009	kansai002	10.0.0.15	RHEL7.2	root	rootPass
ServerName010	kansai003	10.0.0.16	RHEL6.5	root	rootPass
ServerName011	kansai004	10.0.0.17	RHEL5.5	root	rootPass
ServerName012	kansai005	10.0.0.18	WinServer2008	Administrator	adminPass
ServerName013	kansai006	10.0.0.19	WinServer2012	Administrator	adminPass
ServerName014	kansai007	10.0.0.20	WinServer2016	Administrator	adminPass
```
</div></details>
<details><summary>読み込むリストファイル(ゲストOSサーバ側)</summary><div>

```list:gst_host.list
GuestServer001	guest001	10.10.10.1	RHEL7.4	root	rootPass
GuestServer002	guest002	10.10.10.2	RHEL7.2	root	rootPass
GuestServer003	guest003	10.10.10.3	RHEL6.5	root	rootPass
GuestServer004	guest004	10.10.10.4	RHEL5.5	root	rootPass
GuestServer005	guest005	10.10.10.5	WinServer2008	Administrator	adminPass
GuestServer006	guest006	10.10.10.6	WinServer2012	Administrator	adminPass
GuestServer007	guest007	10.10.10.7	WinServer2016	Administrator	adminPass
```
</div></details>
ツールで使用するファイルは上記の3つ
MainMenu.pyはツール本体
mng_host.list / gst_host.listは各サーバの情報が記載されている。
記載方法は左からホスト和名・ホスト名・IPアドレス・OS種別・ユーザ名・パスワードの順でタブ区切りで記載していく
リストファイルのファイル名は現状では固定で作成する必要がある。

実際の画面は下記の通り
![MngWin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/560464/b73b1745-44ce-567e-e14c-3787d6fdb0ed.png)
![MngLin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/560464/5f325f5d-8d2d-dc3a-69c7-a1e39b509207.png)
![GstWin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/560464/a2ff729a-c761-e54f-7379-e9d388dae2e7.png)
![GstLin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/560464/cc639091-f757-06d6-635f-94c32e7022ab.png)


#・今後の方針
###～要実装～

- 肝心の対象サーバへの接続処理が未実装。
 - Pythonでの外部コマンドの利用法の調査。
 - GUIのプロンプト部への進捗・メッセージ出力をリアルタイム出力方法の調査。
 - PowerShellでの接続処理は確立している為、流用出来ればそれで済ませてしまいたい。

###～改善～

- リストファイルの読み込み用処理(ウィンドウ上にボタン追加)追加
 - 一度リストファイルが読み込めなかった場合再読み込みする術がない(リストファイルの記述方法が誤っていた場合など)。
 - リストファイルを読み込む前提条件が下記の通り存在する。これを満たさないと読み込んでくれない為どうにかする。
     - MainMenu.pyとリストファイルが同じフォルダ内にいる必要がある。
     - リストファイルのファイル名が現状固定で、ファイル名が異なるとエラーを吐いてしまう。

- 接続するアカウントを選択できるようにする。
 - 使用するMWによりアカウントを別にする必要がある場合があるのでそれに対応できるようにしたい。
     - ping疎通OK後別ウィンドウで使用するアカウントを選択する方式が楽・・・かな？

###～要検証～

- コンパイルすればPython入れてなくても動くという曖昧な知識で見切り発車しただけなので実際動くのかの検証
 - ダメだったら・・・まぁPython入れてもらえばいいんじゃねぇかな。せっかく作ったし。