URAMIRAIKAN

1020のなれの果て (since 2005.6.19)

VBSでIPアドレスを操作

 どうしてこんなものを作ることになってしまったのか…、という自戒を込めて。

 とある古いシステムを簡易的に冗長化することになり、HAクラスタみたいに共有IPアドレスを切り替える仕組みを作ることになりました。
 その一環で作ったIPアドレスを切り替えるスクリプトのメイン部分です。

 基本的な動きは、概ね以下の通り。

  1. 共有IPアドレスを自分が持っている場合は削除
  2. 共有IPアドレスが存在しない場合は自分に設定
  3. 共有IPアドレスを他のサーバが持っている場合は何もしない
  4. 引数を与えて強制的に自分にIPアドレス設定または削除をする
Option Explicit On Error Resume Next 'サーバ固有IPアドレス Const LocalIp = "192.168.0.11" '共有VIP Const SharedVip = "192.168.0.10" 'サブネットマスク(共通) Const Subnet = "255.255.255.0" Const GuardFile = "guard.tmp" Dim objShell, objFS Dim strArg, intIpStat, bolResult, i Set objShell = WScript.CreateObject("WScript.Shell") Set objFS = WScript.CreateObject("Scripting.FileSystemObject") objShell.CurrentDirectory = objFS.GetParentFolderName(WScript.ScriptFullName) & "\." 'Main Routine '多重実行防止 If objFS.FileExists(GuardFile) Then WScript.Quit(0) '引数の判定 If WScript.Arguments.Unnamed.Count = 0 Then 'VIPチェック intIpStat = IpCheck(SharedVip) Else Select Case WScript.Arguments.Unnamed(0) Case "Assign" If VipAssign(LocalIp, SharedVip, Subnet) Then WScript.Quit(0) Case "Remove" If VipRemove(LocalIp, SharedVip, Subnet) Then WScript.Quit(0) Case Else WScript.Echo "Usage: ""Assign"" or ""Remove""" End Select WScript.Quit(255) End If If intIpStat = 0 Then objFS.CreateTextFile GuardFile, True '自ノードがVIPを保持している場合は削除 If VipRemove(LocalIp, SharedVip, Subnet) Then WScript.Sleep(360000) objFS.DeleteFile GuardFile, True WScript.Quit(0) End If objFS.DeleteFile GuardFile, True WScript.Quit(1) ElseIf intIpStat = 1 Then '他ノードVIPを保有している場合は何もせず終了 WScript.Quit(0) Else 'ネットワーク上にVIPが存在しない場合は自ノードに付与 i = 0 Do While True bolResult = VipAssign(LocalIp, SharedVip, Subnet) WScript.Sleep(3000) intIpStat = IpCheck(SharedVip) If intIpStat = 0 Then WScript.Quit(0) End If If i > 3 Then WScript.Quit(1) End If i = i + 1 WScript.Sleep(10000) Loop End If WScript.Quit(0) 'Sub Routine 'VIPチェック (0 - Assign, 1 - Exist, 2 - Not exist) Function IpCheck(TargetIpAddress) Dim strIpAddress, strNic, strPingItem, colNics, colPingItems, RetVal RetVal = False IpCheck = 2 Set colPingItems = GetObject("winmgmts:").ExecQuery("SELECT * FROM Win32_PingStatus WHERE Address = '" & TargetIpAddress & "'") For Each strPingItem In colPingItems If strPingItem.StatusCode = 0 Then RetVal = True IpCheck = 1 End If Next Set colPingItems = Nothing If RetVal Then Set colNics = GetObject("winmgmts:").ExecQuery("Select * From Win32_NetworkAdapterConfiguration Where IPEnabled=True") For Each strNic In colNics For Each strIpAddress In strNic.IPAddress If strIpAddress = TargetIpAddress Then IpCheck = 0 End If Next Next Set colNics = Nothing End If End Function 'VIP消去 (True/False) Function VipRemove(LocalIpAddress, SharedIpAddress, SubnetMask) Dim strIpAddress, strNic, colNics, RetVal Dim arrIpList(0), arrSubnet(0) arrIpList(0) = LocalIpAddress arrSubnet(0) = SubnetMask Set colNics = GetObject("winmgmts:").ExecQuery("Select * From Win32_NetworkAdapterConfiguration Where IPEnabled=True") For Each strNic In colNics For Each strIpAddress In strNic.IPAddress If strIpAddress = SharedIpAddress Then RetVal = strNic.EnableStatic(arrIpList, arrSubnet) End If Next Next Set colNics = Nothing If RetVal = 0 Then VipRemove = True Else VipRemove = False End If End Function 'VIP追加 (True/False) Function VipAssign(LocalIpAddress, SharedIpAddress, SubnetMask) Dim strIpAddress, strNic, colNics, RetVal Dim arrIpList, arrSubnet arrIpList = Array(LocalIpAddress, SharedIpAddress) arrSubnet = Array(SubnetMask, SubnetMask) Set colNics = GetObject("winmgmts:").ExecQuery("Select * From Win32_NetworkAdapterConfiguration Where IPEnabled=True") For Each strNic In colNics If strNic.IPAddress(0) = LocalIpAddress Then RetVal = strNic.EnableStatic(arrIpList, arrSubnet) End If Next Set colNics = Nothing If RetVal = 0 Then VipAssign = True Else VipAssign = False End If End Function

 IPアドレスを持っているかどうかの判定は、下記のように自分自身のIPアドレスとPingの結果で確認していますが、この辺に何か穴がありそうです。

  • 共有IPアドレスのPing応答がない → IPアドレスが存在しない
  • Ping応答はあるが自分のIPアドレスではない → 他のサーバが保持
  • Ping応答があり自分がIPアドレスを保持 → 自分が持っている

 無論これだけでは冗長化は成り立たないので、サービスの再起動や障害を検知する仕組み、障害条件による分岐などが必要です。
 しかもお客様の要望で、Zabbixと組み合わせて障害時の切り替えを自動化することに…。
 一応、簡易的なものでできないことも多々あるということで納得してもらっていますが、いずれ障害が起こったときに切り替わらなかったとか、もしくは障害ではないのに切り替わりが発生したとか、何か起こりそうで恐いです。

 ちゃんとしたクラスタソフトを購入した方が信頼できるし、結果的に安上がりですよね?