MortenM Posted May 28, 2014 Posted May 28, 2014 Hi, I'm trying to find a way to run a command on several computers at once, but so far my solution is to use batch files for part of the script. One thing I try to do is check if the computers are online, but I do not wish to wait for a response from each computer before checking the next. In this case I now use a batch file with a "for" loop and calling another batch file using "start /b". Then I use a Third batch file to collect the data in a single log file I can display. It's not a pretty process, but it works. Func CheckFileExist() DirRemove("tmp", 1) RunWait("Process1.bat", "", @SW_HIDE) For $i = 0 To 90 GUICtrlSetData($NetworkProgressBar, $i) Sleep(30) Next RunWait("Process2.bat", "", @SW_HIDE) GUICtrlSetData($NetworkProgressBar, 95) _ReplaceStringInFile("tmp\temp.2tmp", "|0", "|Online") _ReplaceStringInFile("tmp\temp.2tmp", "|1", "|!.....Offline.....!") _FileReadToArray("tmp\temp.2tmp", $aNetworkOutput) DirRemove("tmp", 1) _GUICtrlListView_DeleteAllItems($NetworkListView) EndFunc ;==>CheckFileExist Process1.bat @echo off for /f %%a in (list.txt) do start /b CheckForFile.bat %%a CheckForFile.bat @echo off dir \\%1\c$\octopus\bin\appmon.exe > nul 2<&1 echo %1^|%errorlevel% > %1.tmp exit Process2.bat @echo off for %%b in (*.tmp) do type %%b >> temp.2tmp del *.tmp -Y List.txt is a list of the computers in the network. Is there a way to acheive this using only AutoIt? Another case I do a similar thing is when checking cpu load. I use 2 batch files for this. Func GetLoad() DirRemove("tmp", 1) RunWait("cpuload1.bat", "", @SW_HIDE) For $i = 0 To 99 GUICtrlSetData($LoadProgressBar, $i) Sleep(80) Next RunWait("cpuload2.bat", "", @SW_HIDE) _FileReadToArray("tmp\cpuload.2tmp", $aLoadOutput) DirRemove("tmp", 1) _GUICtrlListView_DeleteAllItems($LoadListView) EndFunc ;==>GetLoad cpuload1.bat @echo off REM Delete any old files rmdir tmp /s/ /q 2>nul REM Creating directory for tmp files mkdir tmp 2>nul REM Get processor time value for /f %%i in (list.txt) do start "" /b typeperf \\%%i "Processor(_Total)\%% processor time" -sc 4 -o tmp\%%i.tmp -y >nul cpuload2.bat REM @echo off setlocal enabledelayedexpansion REM Extract and show relevant processor value for /f %%a in (list.txt) do ( for /f tokens^=4^ delims^=.^"^ skip^=4 %%b in (tmp\%%a.tmp) do @echo %%a^|%%b %%^|!time! ) >> tmp\cpuload.2tmp Same thing here, I would like to do this using only AutoIt, and avoid using temporary files.
UEZ Posted May 28, 2014 Posted May 28, 2014 You mean online / offline when remote system is pingable? If yes you can use Ping function but this will work only sequentially. Br, UEZ Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ
MortenM Posted May 28, 2014 Author Posted May 28, 2014 Yes, online/offline status is one thing I need. I tried using ping, but it is painfully slow and as you mention will only work sequentially.
AdamUL Posted May 28, 2014 Posted May 28, 2014 I had a similar project a while back, but it was to check to see if a user was logged on or not. Since AutoIt is not multi-threaded, I used multi-processing. I created a small script to just check the logged on user status using WMI and PLINK. I then used the main script to run the smaller script for each computer name. It would loop through a list of computer names and check around 120 computers at nearly the same time, and have a time out (20 s) to close any open processes. I used the exit codes of the smaller script to determine if the user was logged on or not. On average it took about 40 seconds to a complete a loop. Since you are just checking online status, it shouldn't be as complicated. Adam
Gianni Posted May 28, 2014 Posted May 28, 2014 (edited) Hi MortenM if you want ping a lot of computers, you can use >this UDF (it pings up to 20-25 clients in parallel) with that, you can also perform your own routine against each client that responds to the ping as soon as you get the ping response by just passing the name of your function to the udf. a simple example of use is included there. >here another simple example of use (view onlin/offline clients by green/red boxes) Edited May 29, 2014 by Chimp Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....
MortenM Posted May 30, 2014 Author Posted May 30, 2014 Thanks, I will look into both these suggestions. I have just below 100 computers I want to get the status from. Why is there a limitation on the amount of computers you can ping in parallel with the multiping UDF? Do you have any suggestions on how I can get CPU load of the remote computers in a better way? I would also need to run this on many computers in parallel, but I can adjust the multiping UDF or use multiprocessing to acheive this. Thank you.
Gianni Posted May 30, 2014 Posted May 30, 2014 Thanks, I will look into both these suggestions. I have just below 100 computers I want to get the status from. Why is there a limitation on the amount of computers you can ping in parallel with the multiping UDF? Do you have any suggestions on how I can get CPU load of the remote computers in a better way? I would also need to run this on many computers in parallel, but I can adjust the multiping UDF or use multiprocessing to acheive this. Thank you. Hi MortenM I think that if you spawn too many ping processes simultaneously, then performances will worsen instead of improve (however you can modify the line Local $MAX_PROCESS = 20 in that UDF if you want test different values) anyway, the limit of 20-25 is on the simultaneously running ping and not on the total number of IP that you can ping. They are executed 20 at a time and in few second you will get result of all of your 100 devices. In addition, I also "disconnected" from the whole MultiPing, only a general portion to run at the same time not only ping commands, but any list of commands. I will reorganize it a bit and will post here Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....
Kovacic Posted May 30, 2014 Posted May 30, 2014 (edited) this is what I did.. I have several computers and a server I use thats holds files for those computers... The client machines send a 'beacon' to the file that corisponds to that machine, and my personal computer keeps an eye on that list of files... example: CLIENTs: AdlibRegister ( "beacon" , 90000) Global $LiveFile = "\\MyServer\TEMP\" & @computername & ".ini" ; Code that puts all sorts of into ino the ini file for me Func beacon() FileSetTime($LiveFile, "" ) EndFunc On the server, I have a list view that shows all files in a list ( not including the ini ), along with the details I need, and finally the last modified date and time. FileSetTime uses VERY little bandwidth, and updates the last updated time of the ini file for that machine so I know when it was last touched. example of the function I used for my update list on my personal client machine: func RefreshCompList() ; READY clearit() ; clears lists $FileList = "" $FileList =_FileListToArray("\\MyServer\TEMP") $fileloc = "\\MyServer\TEMP\" for $i = 1 to ubound($FileList) - 1 local $inifile = $FileList[$i] If Stringinstr(Stringreplace($inifile,".ini",""), _GUICtrlListView_GetItemTextArray($MachList,0)) then ContinueLoop if FileGetTime($fileloc & $FileList[$i], $FT_MODIFIED, 1) then $modtime = DTFormat(FileGetTime($fileloc & $FileList[$i], $FT_MODIFIED, 1)) else $modtime = "Unknown" endif $stat = IniRead($fileloc & $FileList[$i], "Status", "Current_Status", "TBD") $DateVar = _DateDiff( 's',FileDTFormat(FileGetTime($fileloc & $FileList[$i], $FT_MODIFIED, 1)) ,_NowCalc()) GUICtrlCreateListViewItem( Stringreplace($inifile,".ini","") & "|" & $stat & "|" & $modtime , $MachList) GUICtrlSetBkColor(-1, 0xA3FF75) If $DateVar > 120 then GUICtrlSetBkColor(-1, 0xFFF79) If $DateVar > 180 then GUICtrlSetBkColor(-1, 0xFF9933) If $DateVar > 240 then GUICtrlSetBkColor(-1, 0xFF0000) next ;_ArrayDisplay($FileList,"$FileList") endfunc Obviously you would have the change the variables as needed Edited May 30, 2014 by Kovacic C0d3 is P0etry( ͡° ͜ʖ ͡°)
MortenM Posted June 2, 2014 Author Posted June 2, 2014 Thanks for the great replies. I am trying to understand the code and simplify it to suit my needs instead of just copying the functions. I will update with my code when I have something that may work.
MortenM Posted June 3, 2014 Author Posted June 3, 2014 This is what I ended up doing, and it works great: expandcollapse popupGUICreate("Network test", 500, 500) $NetworkUpdateBtn = GUICtrlCreateButton("Update", $NetworkLeft - 50, $GuiHeight - 50, 65, 30) $NetworkProgressBar = GUICtrlCreateProgress($NetworkLeft + 20, $GuiHeight - 50, $GuiWidth - 100, 30) GUISetFont(8) $NetworkListView = GUICtrlCreateListView("Name|IP|Status", $NetworkLeft - 50, $NetworkTop, $GuiWidth - 30, $GuiHeight - 80) _GUICtrlListView_SetColumnWidth($NetworkListView, 0, 130) _GUICtrlListView_SetColumnWidth($NetworkListView, 1, 130) _GUICtrlListView_SetColumnWidth($NetworkListView, 2, 130) Dim $NET_DESCENDING[_GUICtrlListView_GetColumnCount($NetworkListView)] GUISetState(@SW_SHOW) While 1 $msg = GUIGetMsg() Select Case $msg = $GUI_EVENT_CLOSE ExitLoop Case $msg = $NetworkUpdateBtn $PingArray = _nPing($aCompNameIP, 1, 0, 0, Default) _ArrayDelete($PingArray, 0) For $n = 0 To UBound($aCompNameIP) - 1 _GUICtrlListView_AddItem($NetworkListView, $aCompNameIP[$n][0], -1, $n + 9999) _GUICtrlListView_AddSubItem($NetworkListView, $n, $aCompNameIP[$n][1], 1) If $PingArray[$n][2] = -1 Then $PingArray[$n][2] = "Offline" Else $PingArray[$n][2] = "Online - Ping " & $PingArray[$n][2] & "ms" EndIf _GUICtrlListView_AddSubItem($NetworkListView, $n, $PingArray[$n][2], 2) Next Case $msg = $NetworkListView _GUICtrlListView_SimpleSort($NetworkListView, $NET_DESCENDING, GUICtrlGetState($NetworkListView)) EndSelect WEnd Thanks for all the help. I still need to figure out how to fix the CPU load function without calling external batch files, but one Down at least
Gianni Posted June 4, 2014 Posted June 4, 2014 (edited) Hi MortenM Glad you achieved part of your goal by using the _nPing function in multiping.udf. as I told in post #7, here is a scaled down version of the manadar's nping minimized and adapted to run any DOS command instead of only specialized to PING as the original (I called Multi_DOS() in the listing). It will spawn up to 25 command at time in parallel to speed up job. I arranged a bit the listing with comments, but is still a bit a draft. In this example I used the wmic command to get cpu load percentage from remote clients. I used 6 clients all with the same hostname (local client) just to show a proof of concept (I know that it's a nonsense) but I had not idea of other hostnames to use as example. In real situations many remote clients will be used instead of course. expandcollapse popup#include <Constants.au3> #include <array.au3> ; example of array with 6 hostnames ; (this is an useless example because the 6 clients are all the same, just to show this example) ; in normal real situations the clients in the list are much more and all different of course ; ; the array can be filled by reading a file from disk, or by scanning a network using Multiping.au3 for example. ; Local $onLine[6] = [@ComputerName, @ComputerName, @ComputerName, @ComputerName, @ComputerName, @ComputerName] ; ; few local commands just to test. ; now we prepare the array (must have 4 columns) that will hold commands to run against all clients Global $Commands[UBound($onLine)][4] ; we use the wmic command to read cpu load of all clients in the list ; so we fill the column 1 of the array with the following command ; (one line for each client) ; For $i = 0 To UBound($onLine) - 1 $Commands[$i][0] = $onLine[$i] ; name of the client just for reference $Commands[$i][1] = 'wmic /node:' & $onLine[$i] & ' cpu get loadpercentage /value' Next ; this is the array with all commands personalized for each client _ArrayDisplay($Commands, "before run" & @CRLF) Multi_DOS($Commands) ; here we fire all commands running 25 at time ; and this is the array with columns 2 and 3 filled by the Multi_DOS udf _ArrayDisplay($Commands, "after run" & @CRLF) ; the end ; --- here is a modified and reduced portion of nping by manadar --- ; --- adapted tu run any dos command instead of only PING --- ; ; array must be a 2D arry with 4 columns [n][4] (dimension [n] as many elements as nr. of DOS commands to run ; ; the functions accepts an array ByRef loaded with commands to run in column 1 [n][1] ; the function in turn will fill the array elements [n][2] or [n][3] as follows: ; ; [n][2] with the autput of the StdOut of the passed command ; [n][3] with the autput of the StdErr of the passed command ; ; ; | [n][0] | [n][1] provided by user | [n][2] filled by func | [n][3] ] filled by func | ; +-------------------------+-------------------------+-------------------------+-------------------------+ ; | [user defined value] | DOS command to run | DOS outpu (StdOut) | DOS error (StdErr) | ; +-------------------------+-------------------------+-------------------------+-------------------------+ Func Multi_DOS(ByRef $cmd) If Not IsArray($cmd) Or UBound($cmd, 2) < 3 Then Return SetError(1) ; must be an array with second dim >= 3 Local $MAX_PROCESS = 25 ; A maximum number of processes Local $iTotal = UBound($cmd) ; Number of commands to run Local $Process[$MAX_PROCESS][2] ; array to track the simultaneously running commands (internal use) ; [0] PID of DOS spawn ; [1] index to bind this process to the command index in the $cmd array For $i = 0 To $MAX_PROCESS - 1 $Process[$i][0] = 0 $Process[$i][1] = 0 Next Local $i = 0 Local $iFinished = 0 ; how many processes have finished run While 1 For $n = 0 To UBound($Process) - 1 ; ; We check all the currently running processes ; Check if we need a spot, and there is an existing spot here If ($i <> $iTotal And $Process[$n][0] = 0) Then ; if there are still commands to be run and if there is a free spot ; Spawn a new process in the available spot $Process[$n][0] = Run(@ComSpec & " /c " & $cmd[$i][1], "", @SW_HIDE, $STDOUT_CHILD + $STDERR_CHILD) ; spawn a new command $Process[$n][1] = $i ; take trak of commands $i += 1 ; Increment $i so we can run next process next time around Else ; Check if this process has been spawned and the process is ready If $Process[$n][0] <> 0 Then ; is this command still running? ; collect output from this command $cmd[$Process[$n][1]][2] &= StdoutRead($Process[$n][0]) $cmd[$Process[$n][1]][3] &= StderrRead($Process[$n][0]) If (@error) Then ; has this command terminated? $Process[$n][0] = 0 ; Free up an empty space for the next command to run $iFinished += 1 ; Increment the counter of processes that have finished ; possible to call() a function here to notify the end of this command ; ando/or perform some action (a sort of "OnfFnished" event) ; Else EndIf If ($iFinished == $iTotal) Then ExitLoop 2 ; the end EndIf EndIf Next Sleep(50) ; Give existing commands some time to process the request WEnd ; the end of job EndFunc ;==>Multi_DOS Edited June 4, 2014 by Chimp Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....
Gianni Posted June 4, 2014 Posted June 4, 2014 I had a similar project a while back, but it was to check to see if a user was logged on or not. Since AutoIt is not multi-threaded, I used multi-processing. I created a small script to just check the logged on user status using WMI and PLINK. I then used the main script to run the smaller script for each computer name. It would loop through a list of computer names and check around 120 computers at nearly the same time, and have a time out (20 s) to close any open processes. I used the exit codes of the smaller script to determine if the user was logged on or not. On average it took about 40 seconds to a complete a loop. Since you are just checking online status, it shouldn't be as complicated. Adam Hi AdamUL could you post that script? thanks Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....
AdamUL Posted June 5, 2014 Posted June 5, 2014 (edited) Hi Chimp,I currently do not have the full source with me, as it was part of a larger project. I will look to see if I can find it when I get back to work. The code would need to be cleaned up, as this was wrote with an older version of AutoIt, and has some proprietary code in it that i cannot release. I did post an example script for accessing iMac coumputer that I used in the project. Adam Edited May 23, 2015 by AdamUL Broken Link
AdamUL Posted June 5, 2014 Posted June 5, 2014 (edited) Chimp,I have posted it in the Example Scripts. Adam Edited May 23, 2015 by AdamUL Broken link.
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now