Thursday, April 10, 2008

How to update or configure automatically vmware esx or linux servers when you are a windows user ?

Problem when you have 32 esx 3.01 to upgrade is that takes long time. And ask to windows prod team to realize these - not interesting - tasks needs a really big documentation to avoid errors. So I decided to create something to automatize this process.
So, today I give you a script which permit you to update or configure any linux/vmware redhat/unix server with simple vbscript file. First, you need at least plink.exe and pscp.exe from http://www.chiark.greenend.org.uk/~sgtatham/putty/ (Alternative Download). Put these files in the same directory than you will put update_esx.vbs file.

But why this script is magic? Because plink and pscp could do it for you really easily, no ? In fact, problem with plink.exe is when you need to automatize your process. Each time you will try to log on a new server, plink will ask you to create ssh host keys. (for security)
With this script you do no need to create plink ssh host keys before running script on your servers. This script do it for you.
Now, you need to create servers.txt file containing all servers you need to update or configure. (1 line = 1 server). For example:

esxserver001
esxserver002


You need also a .sh file which will be executed on esx - linux server (don't forget to use a unix text file with only line feed, and not Windows text file with line feed and carriage return ASCII characters. So do not use notepad to modify files. Use Textpad or other tools that recognize file encoding. )

Below an example to send to your esx. This file ls.sh

#!/bin/sh
# Configure ESX Server
ls


Last thing: root password should be the same on each esx - linux server (else modify my vbscript using a second column in servers.txt like server;rootpassword)
You must also permit root user to log on your esx.

How to use it: run in you windows command line something like


cscript //nologo update_esx.vbs servers.txt file_to_execute.sh your_esx_root_password


If you need to write log, redirect standard output to a file like this:


cscript //nologo update_esx.vbs servers.txt file_to_execute.sh your_esx_root_password > mylog.txt


And now the last but not least, the main tool, update_esx.vbs

'
' update_esx.vbs
'
' by F.RICHARD
'
' v1.00 2007 Nov - Initial release
' v1.01 2008 Jan - Display Standard output
'
Option Explicit

Const ForReading = 1

'
' Detect WScript or CScript
'
If InStr(LCase(WScript.FullName), "wscript")>0 Then ' ex: FullName = C:\WINNT\system32\wscript.exe
WScript.Echo "This script must be run under CScript."
WScript.Quit
End If

'
' Get Command Line Args
'
Dim Args
Set Args = Wscript.Arguments
If Args.Count < 3 Then
Wscript.Echo "Syntax : " & WScript.ScriptName & " input_computer_list_file script_to_execute root_password"
Wscript.Echo " Example : cscript " & WScript.ScriptName & " servers.txt update.sh password"
Wscript.Echo ""
Wscript.Echo "computer_list_file: line begin by # or ; indicate start of a comment, everything after is ignored"
Wscript.Echo ""
Wscript.Quit
End If


' Arg(0) = computer list
Dim strInputList
strInputList = Trim(args(0))

' Arg(1) = script to execute
Dim strScriptToExecute
strScriptToExecute = Trim(args(1))


' Arg(2) = root password
Dim strPassword
strPassword = Trim(args(2))


'
' Get Path
'
Dim strScriptFullName, strScriptPath
strScriptFullName = WScript.ScriptFullName ' ex: C:\Program Files\MyFolder\MyProg.exe
strScriptPath = Left(strScriptFullName, InStrRev(strScriptFullName, "\") - 1) ' ex: C:\Program Files\MyFolder

Dim objFso
Set objFso = CreateObject("Scripting.FileSystemObject")


'
' Test If all Files Exist
'
If Not isFileExist(strInputList) Then
strInputList = strScriptPath & "\" & strInputList
If Not isFileExist(strInputList) Then
WScript.Echo "Computer list:" & strInputList & " File Not Exist !"
WScript.Quit
End If
End If
WScript.Echo "Computer list:" & strInputList & " File Exist"

If Not isFileExist(strScriptToExecute) Then
strScriptToExecute = strScriptPath & "\" & strScriptToExecute
If Not isFileExist(strScriptToExecute) Then
WScript.Echo "Script to execute:" & strScriptToExecute & """" & " File Not Exist !"
WScript.Quit
End If
End If
WScript.Echo "Script to execute:" & strScriptToExecute & " File Exist"

Dim strPlink
strPlink= strScriptPath & "\" & "plink.exe"
If Not isFileExist(strPlink) Then
WScript.Echo "PuTTY Link:" & strPlink & " File Not Exist !"
WScript.Quit
End If
WScript.Echo "PuTTY Link:" & strPlink & " File Exist"

Dim strPscp
strPscp= strScriptPath & "\" & "pscp.exe"
If Not isFileExist(strPscp) Then
WScript.Echo "PuTTY Secure Copy client:" & strPscp & " File Not Exist !"
WScript.Quit
End If
WScript.Echo "PuTTY Secure Copy client:" & strPscp & " File Exist"


Dim objShell, ComSpec
Set objShell = CreateObject( "WScript.Shell" )
ComSpec=objShell.ExpandEnvironmentStrings("%ComSpec%")

'
' Read computer list file into dictionary
'
Dim objDictionary, return
return = ReadFileToDict(strInputList, objDictionary)
If return = 0 Then
WScript.Echo "Error during computer list file read"
WScript.Quit
End If

' Display file
Dim objItem, strServer, objCmd, strCmdline, continue, strGenerateKey
For Each objItem in objDictionary
strServer = objDictionary.Item(objItem)
Wscript.Echo ""
WScript.Echo "Server:" & strServer

' Test Connection
strCmdline = ComSpec & " /c """ & strPlink & """ -batch -C -pw " & strPassword & " root" & "@" & strServer & " pwd "
strGenerateKey = ComSpec & " /c echo y | """ & strPlink & """ " & strServer & " -pw " & strPassword & " root"

WScript.Echo "Test Connection on server " & strServer
return = DoConnection(strCmdline, strGenerateKey, strServer, True)

If (return > 0) Then
strGenerateKey=""
Do
WScript.Echo "Re-verify Connection on server " & strServer
return = DoConnection(strCmdline, strGenerateKey, strServer, True)
Loop While (return > 0 and return < 255)
If (return > 0) Then
WScript.Echo "Transfer " & strScriptToExecute & " script on server " & strServer
strCmdline = ComSpec & " /c """ & strPscp & """ -batch -C -pw " & strPassword & " " & strScriptToExecute & " root" & "@" & strServer & ":/tmp"
return = DoConnection(strCmdline, strGenerateKey, strServer, True)

WScript.Echo "Execute " & strScriptToExecute & " script on server " & strServer
strCmdline = ComSpec & " /c """ & strPlink & """ -batch -C -pw " & strPassword & " root" & "@" & strServer & ":/tmp" & " sh /tmp/" & strScriptToExecute
return = DoConnection(strCmdline, strGenerateKey, strServer, True)
End If
End If

Next


'
' Quit
'
WScript.Sleep 1000
WScript.Quit



'--------------------

' DoConnection
'
'
'
Function DoConnection(strCmdline, strGenerateKey, strServer, displayLogfile)
Dim objCmd, return, Continue

'WScript.Echo "cmdline->" & strCmdline
Set objCmd = objShell.Exec(strCmdline)
Do While objCmd.Status = 0
return = objCmd.stdErr.ReadAll
WScript.Sleep 100
Loop
If (displayLogfile) Then
Dim strStdOut
strStdOut = objCmd.StdOut.ReadAll()
If (len(strStdOut) > 0) Then
Wscript.Echo strStdOut
End If
End If

continue = 0
If Instr(return, "Access denied" ) > 0 Then ' Access denied
WScript.Echo "ERROR on server " & strServer & vbCrLf & return
continue = 0

Elseif Instr(return, "Unable to open connection" ) > 0 Then ' Unable to open connection
WScript.Echo "ERROR on server " & strServer & vbCrLf & return
continue = 0

Elseif Instr(return, "ERROR" ) > 0 Then ' FATAL ERROR: Network error: Connection timed out
WScript.Echo "ERROR on server " & strServer & vbCrLf & return
continue = 0

Elseif Instr(return, "fingerprint" ) > 0 Then
If (len(strGenerateKey) > 2) Then
WScript.Echo "GENERATE key fingerprint on server " & strServer
'WScript.Echo "generatekey->" & strGenerateKey
objShell.Exec(strGenerateKey)
continue = 1
Else
WScript.Echo "NOT GENERATE key fingerprint " & strServer
continue = 2
End If
Else
continue = 255
'WScript.Echo "Logged on " & strServer & " - " & return
End if

DoConnection = continue
End Function



'--------------------

' isFileExist
'
' Test if File Exist
'
Function IsFileExist(strInputFile)
Dim objFSO
Set objFSO = CreateObject("Scripting.FileSystemObject")
If Not objFSO.FileExists(strInputFile) Then
Wscript.echo strInputFile & " file not exist !"
IsFileExist = False
Else
IsFileExist = True
End If
End Function


'--------------------

' ReadFileToDict
'
' Read each line -> put in Dictionary
' strInputfile = file to read
' objDictionary = dictionary for results
'
Function ReadFileToDict(strInputFile, ByRef objDictionary)
Dim objFSO, objTextFile, strNextLine, i
Set objDictionary = CreateObject("Scripting.Dictionary")
Set objFSO = CreateObject("Scripting.FileSystemObject")
If isFileExist(strInputFile) Then
Set objTextFile = objFSO.OpenTextFile(strInputFile, ForReading)
i = 0
Do Until objTextFile.AtEndOfStream
strNextLine = Trim(objTextFile.Readline)
If ( (Not Left(strNextLine, 1) = "#") _
and (Not Left(strNextLine, 1) = ";") _
and (Len(strNextLine)>1) _
) Then
objDictionary.Add i, strNextLine
i = i + 1
End If
Loop
objTextFile.Close
ReadFileToDict = True
Else
ReadFileToDict = False
End If
End Function


'--------------------



You can download a .zip file here including an example with change_ntp.sh

Next step, if I had some time, a graphical interface with a description of each script and variables for each script.