IT井戸端会議

読者です 読者をやめる 読者になる 読者になる

IT井戸端会議

インフラ、ネットワーク、アプリケーション開発、IT界隈の話等々を東京都千代田区界隈から発信します。

Perlスクリプトの汎用化+GUI化+exe化=アプリ誕生!(2)

みなさまこんにちは。

前回、GUIを作成したPerlスクリプトの続きです。

 

<今回やること>

GUIを表示させるのみのスクリプトに、コマンド(処理)を追加

Perlから実際にGUIを動かしてみる

 

では、行きます。

 

GUIを表示させるのみのスクリプトに、コマンド(処理)を追加

 

前回作成した、GUIが表示されるだけでボタンを押しても無反応なスクリプトに、ボタンクリック時の処理を紐付けていきます。

若干話が前後してしまいますが、そもそも、今回書いていたスクリプトの機能としては、次のようなものでした。

■作業対象機器のIPアドレスリストを読み込む ■読み込んだIPアドレスに接続し、コマンドを実行 ■コマンドの実行結果をログとしてファイルに出力

 

ですので、機能としては、次のようなGUI要素があればよいことになりますね。

IPアドレスリストをファイル選択するダイアログを表示→リストファイル名を取得するボタン ■コマンドリストをファイル選択するダイアログを表示→リストファイル名を取得するボタン ■ログの出力先フォルダを選択するダイアログを表示→フォルダ名を取得するボタン ■コマンド実行ボタン ■終了ボタン

 

また、各ファイル名、フォルダ名の取得結果を表示するフィールド(エントリ)があると、分かりやすいし手入力もできてよいので、ボタンの横に並べました。

 

Perl/Tkの世界では、ボタンやテキストラベル、入力フィールドのような各GUI要素を「ウィジェット」と呼びます。

ほとんどの種類のウィジェットには、共通するオプションが用意されています。処理を紐付ける、commandオプションもそのひとつです。

例として、1個目のボタンは次のように定義しています。

 

~~~~

Widget Button1 isa Button

$ZWIDGETS{'Button1'} = $MW->Button( -borderwidth => 2, -font        => 'Meiryo_UI_8_normal_roman_', -padx        => 20, -pady        => 10, -width       => 12, -relief      => 'raised', -takefocus   => 0, -text        => 'アドレスリストの選択', -command => &GetNodeListFile, )->grid( -row    => 1, -column => 0, );

~~~~

「$ZWIDGETS」というのは、GUIビルダであるZoozが定義したハッシュで、すべてのウィジェットがこのハッシュに格納されるようです。

そして、メインウィンドウを表す $MW にButtonとして定義されて、その()の中に各オプションが書かれているつくりになっています。

多くは見た目に関する設定値なのはお分かりいただけると思いますが、各オプションの一番下に

-command => &GetNodeListFile,

が書かれています。これが、ボタンクリック時に実行される処理になります。

「&GetNodeListFile」というのは、このボタン用に定義したサブルーティンで、次のように定義しています。

 

~~~~

「アドレスリストの選択」ボタンの実行コマンド

sub GetNodeListFile{ $NodeListFile = $MW->Win32::GUI::GetOpenFileName(); $NodeListFile = decode("CP932",$NodeListFile);

~~~~

「-command =>」の後に処理を実際に並べて書いてもよいのですが、ボタンによっても処理の量は変わるし、可読性も考えるとサブルーティン化するのがよいでしょうね。

このように、各ボタン毎に「-command =>(サブルーティン名)」、サブルーティンリストの中に実際の処理を書いていくことを繰り返して、ウィジェットスクリプトの処理を必要な分だけ紐付けていきます。前回書いた、コア処理部分は当然、「コマンド実行ボタン」に紐付けられます。

こうして出来上がったコードが、以下になります。

~~~~

!C:\perl/bin/

#

                        

 Cisco機器用連続コマンド実行スクリプト    

 (Telnetを想定・パスワードは各機器共通を想定) 

                        

   Written by Takeru Satoh at APCommunications 

                        

#
#

#

This file was automatically generated by ZooZ.pl v1.2

on Thu Aug 20 10:17:47 2015.

Project: Project 1

File:    D:/ActivePerl_setup/CiscoCmdGUI.zooz

#

#

#

Headers

#

おまじない(構文チェック&警告表示)

use strict; use warnings;

GUI用モジュールのロード

use Tk 804; use Win32; use Win32::GUI; use arybase; use Encode;

Cisco-IOS用モジュールのロード

use Net::Telnet::Cisco::IOS;

念のための文字コード指定

use utf8; use open ':encoding(cp932)'; binmode STDIN, ':encoding(cp932)'; binmode STDOUT, ':encoding(cp932)'; binmode STDERR, ':encoding(cp932)';

#

Global variables

# my (

MainWindow

$MW,

Hash of all widgets

%ZWIDGETS,

IPAddress List File

$NodeListFile,

Command List File

$CmdListFile,

Log Output Folder

$LogFolder,

Telnet User Name

$UserName,

LineVTY Password

$vtyPasswd,

Enable Password

$enPasswd

);

$UserName = '(空欄可)';

#

User-defined variables (if any)

#

#

#

Create the MainWindow

#

#

$MW = MainWindow->new( -title => 'Cisco機器コマンド実行ツール Ver.1.0', -background => '#ffffff', -borderwidth => 2, -relief => 'groove', );

#

#

Load any images and fonts

#

#

ZloadFonts ();

以下、GUIの各要素(ウィジェット)を定義

Widget Label1 isa Label

$ZWIDGETS{'Label1'} = $MW->Label( -background => '#ffffff', -font       => 'Meiryo_UI_8_normal_roman_', -takefocus  => 0, -text       => '下記のパラメータを指定し、「コマンド実行の開始」ボタンをクリックしてください。', )->grid( -row        => 0, -column     => 0, -columnspan => 3, );

Widget Button1 isa Button

$ZWIDGETS{'Button1'} = $MW->Button( -borderwidth => 2, -font        => 'Meiryo_UI_8_normal_roman_', -padx        => 20, -pady        => 10, -width       => 12, -relief      => 'raised', -takefocus   => 0, -text        => 'アドレスリストの選択', -command => &GetNodeListFile, )->grid( -row    => 1, -column => 0, );

Widget Button2 isa Button

$ZWIDGETS{'Button2'} = $MW->Button( -font => 'Meiryo_UI_8_normal_roman_', -padx => 20, -pady => 10, -width       => 12, -relief      => 'raised', -takefocus   => 0, -text => 'コマンドリストの選択', -command => &GetCmdListFile, )->grid( -row    => 2, -column => 0, );

Widget Button3 isa Button

$ZWIDGETS{'Button3'} = $MW->Button( -font => 'Meiryo_UI_8_normal_roman_', -padx => 20, -pady => 10, -width       => 12, -relief      => 'raised', -takefocus   => 0, -text => 'ログ出力フォルダの選択', -command => &GetFolder, )->grid( -row    => 3, -column => 0, );

Widget Label2 isa Label

$ZWIDGETS{'Label2'} = $MW->Label( -background => '#ffffff', -font       => 'Meiryo_UI_8_normal_roman_', -padx       => 20, -pady       => 10, -takefocus  => 0, -text       => 'Telnetユーザー名の指定', )->grid( -row    => 4, -column => 0, );

Widget Label3 isa Label

$ZWIDGETS{'Label3'} = $MW->Label( -background => '#ffffff', -font       => 'Meiryo_UI_8_normal_roman_', -padx       => 20, -pady       => 10, -takefocus  => 0, -text       => 'Line VTYパスワードの指定', )->grid( -row    => 5, -column => 0, );

Widget Label4 isa Label

$ZWIDGETS{'Label4'} = $MW->Label( -background => '#ffffff', -font       => 'Meiryo_UI_8_normal_roman_', -padx       => 20, -pady       => 10, -takefocus  => 0, -text       => 'Enableパスワードの指定', )->grid( -row    => 6, -column => 0, );

Widget Label5 isa Label

$ZWIDGETS{'Label5'} = $MW->Label( -background => '#ffffff', -font       => 'Meiryo_UI_8_normal_roman_', -takefocus  => 0,

   -text       => 'Powered by :',

)->grid( -row    => 8, -column => 0, );

Widget Entry1 isa Entry

$ZWIDGETS{'Entry1'} = $MW->Entry( -font  => 'Meiryo_UI_8_normal_roman_', -width => 30, -textvariable => \$NodeListFile, )->grid( -row        => 1, -column     => 1, -columnspan => 2, );

Widget Entry2 isa Entry

$ZWIDGETS{'Entry2'} = $MW->Entry( -exportselection => 1, -font            => 'Meiryo_UI_8_normal_roman_', -width           => 30, -textvariable => \$CmdListFile, )->grid( -row        => 2, -column     => 1, -columnspan => 2, );

Widget Entry3 isa Entry

$ZWIDGETS{'Entry3'} = $MW->Entry( -exportselection => 1, -font            => 'Meiryo_UI_8_normal_roman_', -width           => 30, -textvariable => \$LogFolder, )->grid( -row        => 3, -column     => 1, -columnspan => 2, );

Widget Entry4 isa Entry

$ZWIDGETS{'Entry4'} = $MW->Entry( -exportselection => 1, -font            => 'Meiryo_UI_8_normal_roman_', -width           => 30, -textvariable => \$UserName, )->grid( -row        => 4, -column     => 1, -columnspan => 2, );

Widget Entry5 isa Entry

$ZWIDGETS{'Entry5'} = $MW->Entry( -exportselection => 1, -font            => 'Meiryo_UI_8_normal_roman_', -width           => 30, -textvariable => \$vtyPasswd, )->grid( -row        => 5, -column     => 1, -columnspan => 2, );

Widget Entry6 isa Entry

$ZWIDGETS{'Entry6'} = $MW->Entry( -exportselection => 1, -font            => 'Meiryo_UI_8_normal_roman_', -width           => 30, -textvariable => \$enPasswd, )->grid( -row        => 6, -column     => 1, -columnspan => 2, );

Widget Button4 isa Button

$ZWIDGETS{'Button4'} = $MW->Button( -font    => 'Meiryo_UI_8_normal_roman_', -justify => 'center', -padx    => 20, -pady    => 10, -text    => 'コマンド実行の開始', -command => &GetLog, )->grid( -row    => 7, -column => 1, );

Widget Button5 isa Button

$ZWIDGETS{'Button5'} = $MW->Button( -font => 'Meiryo_UI_8_normal_roman_', -padx => 20, -pady => 10, -text => '終了', -command => &DestroyWindow, )->grid( -row    => 7, -column => 2, );

$MW->gridRowconfigure(0, -pad => 20, );

$MW->gridRowconfigure(1, -pad => 10, );

$MW->gridRowconfigure(2, -pad => 10, );

$MW->gridRowconfigure(3, -pad => 10, );

$MW->gridRowconfigure(4, -pad => 10, );

$MW->gridRowconfigure(5, -pad => 10, );

$MW->gridRowconfigure(6, -pad => 10, );

$MW->gridRowconfigure(7, -pad => 40, );

#

#

MainLoop

#

#

MainLoop;

#

#

Subroutines

#

#

GUI用フォント設定

sub ZloadFonts { $MW->fontCreate('Meiryo_UI_8_normal_roman_', -slant      => 'roman', -underline  => 0, -weight     => 'normal', -size       => -11, -overstrike => 0, -family     => 'Meiryo UI', ); }

「アドレスリストの選択」ボタンの実行コマンド

sub GetNodeListFile{ $NodeListFile = $MW->Win32::GUI::GetOpenFileName(); $NodeListFile = decode("CP932",$NodeListFile);

}

「コマンドリストの選択」ボタンの実行コマンド

sub GetCmdListFile{ $CmdListFile = $MW->Win32::GUI::GetOpenFileName(); $CmdListFile = decode("CP932",$CmdListFile);

}

「ログ出力フォルダの選択」ボタンの実行コマンド

sub GetFolder{ $LogFolder = $MW->Win32::GUI::BrowseForFolder(); $LogFolder = decode("CP932",$LogFolder); }

「コマンド実行の開始」ボタンの実行コマンド

sub GetLog{

対象ノードのIPアドレスを読み込む

open (NODELIST, "<", $NodeListFile) or die ("error :$!");

IPアドレスひとつずつに対し以下の処理を実行

while (my $nlist = <NODELIST>){ chomp($nlist);

my $IPAddress = $nlist; my $hostname;

セッションの確立

my $conn= Net::Telnet::Cisco::IOS->new( Errmode => "return", Timeout => 20, HOST => $IPAddress);

vtyパスワードを用いてログイン

$conn->login( Name => $UserName , Password => $vtyPasswd );

ENABLEモードに昇格

$conn->enable( $enPasswd );

HOSTNAME取得

my @show_run = $conn->cmd('show run | inc hostname'); for my $i (@show_run){ if($i =~ /hostname/){ $hostname = $i; chomp($hostname); $hostname =~ s/(?:hostname )//g; last; } }

ログファイル名に付加する日付を"yyyymmdd"で生成

my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); my $yyyymmdd = sprintf("%04d%02d%02d", $year + 1900, $mon + 1, $mday);

ログをファイルへ出力

my $file_out = $LogFolder ."/". $hostname . "_" . $yyyymmdd .".txt"; open (LOG, ">>", $file_out) or die "$!";

各ノードで実行するコマンドを記載したリストを読み込む

open (CMDLIST, "<", $CmdListFile) or die ("error :$!");

コマンドリストに記載のコマンドをひとつずつ実行

while (my $clist = <CMDLIST>){ chomp($clist);

my $Command = $clist; my @logging = "\n". $hostname .'>'. $Command . "\n"; @logging = $conn->cmd($Command);

print "\n\n\n\n\n". $hostname .'>'. $Command . "\n"; print @logging;

print ( LOG "\n\n\n\n\n". $hostname .'>'. $Command . "\n"); print ( LOG @logging );

}

ファイルハンドルのクローズ

close CMDLIST; close LOG;

}

プロンプトに完了のメッセージを出力

print "\nログ取得が完了しました。\n"; print "終了するにはEnterキーを押してから、アプリ「終了」ボタンをクリックしてください。\n"; my $input = <STDIN>;

}

「終了」ボタンの実行コマンド

sub DestroyWindow{ $MW->destroy;

}

~~~~

ここで注意というか、理解が必要なのが、次の2つです。

GUIから取得する値はグローバル変数に格納すること。

各ボタンからは「-command」オプションで紐付けられたサブルーティンを呼び出します。しかし、サブルーティン中で定義された変数はそのサブルーティン内でしか使えず、例えばIPアドレスのリストファイル名を取得しても、コマンド実行処理にファイル名を渡せない…ということになってしまいます。

そこで、どのサブルーティンからもアクセスできるグローバル変数にリストファイル名を格納することで、どのサブルーティンからも共通にアクセスできるように書いてあります。GUIから入力される値は、全てそのようにしました。処理の中で頻繁に値が変わったり参照される場合は、グローバル変数で横着しない方がいいのかもしれませんが、今回はそれほど複雑な処理でもありませんしね。

GUIを使うために必要なモジュールを追加でロードしていること。

下記の各モジュールは、スクリプトとして動作させるだけなら不要なのですが、GUIを動作させるのには必要になります。

use Win32; → 下記Win32::GUIを使うのに必要(らしい) ※パッケージ化で改めて触れます。 use Win32::GUI; → OS標準のファイル・フォルダ選択ダイアログを呼び出すのに必要 use arybase; → 配列を使うのに必要(らしい) ※パッケージ化で改めて触れます。 use Encode; →PerlのコードはUTF8で書かれるが、WindowsはShiftJIS(CP932)の世界なので、Windowsから取得したファイル・フォルダ名を扱うのに必要。

 

Perlから実際にGUIを動かしてみる

コマンドプロンプトから、上記のコードを保存した「CiscoCmdTool-ver1.0.pl」を指定して、次の構文で実行します。

コマンドプロンプトで cd c:\perl\bin/ ・C:\Perl\bin\perl.exe "(CiscoCmdTool-ver1.0.pl へのフルパス)"

前回同様のGUIが開き、各ボタンも今度はクリックするとちゃんとダイアログを開いてくれて、動作しているのが分かると思います。

 

またボリュームが多くなってきたので、この辺で。

次回は、いよいよパッケージャのお話です。