WinBatch Tech Support Home

Database Search

If you can't find the information using the categories below, post a question over in our WinBatch Tech Support Forum.

TechHome

NetwareX Extender

Can't find the information you are looking for here? Then leave a message over on our WinBatch Tech Support Forum.

NDS Servers Configurations


Offered as is. Be sure to review thoroughly and adapt and test to your own situation. Use at your own risk. Not responsible for damage, loss of business, etc.

On Netware servers, a CONFIG.NLM program can be run to produce a concise comprehensive server configuration report file CONFIG.TXT, which is useful for server documentation, disaster recovery, etc.

This program collects such files from servers specified in an internal list (the CONFIG.NLM is being run by someone/something else on a regular basis, so all this program needs to do is collect the resulting files) and consolidates them into a single compressed (.zip) file.

Additional code exists in the program, but is commented out, for:

 
- querying NDS for a list of server names (would need to add exemption/filter logic to protect against trying to access problematic/virtual/etc. servers)
- remotely executing the CONFIG.NLM program on those servers (so as to assure up to the minutes results in the files being collected)
Offered as is. Be sure to review thoroughly and adapt and test to your own situation. Use at your own risk. Not responsible for damage, loss of business, etc

;=============================================================================================
; program: 		NDS Servers Configurations 
;
; language: 	WinBatch
; extenders:	NetwareX 		WWNWX34I.DLL
;              Zip Extender	WWZIP34I.DLL

;
; author:		Michael Harris
; created:		6/24/2003		last updated: 7/07/2003
;
; description:
;    Can be run interactively or batch. (Note: Batch mode has not been tested)
;    Ultimately, could query NDS for all server objects (code exists below but is commented out)
;    (if did, would need an exemption/filtering mechanism to exempt problematic, non-Netware,
;     virtual, etc. servers)
;    (but for now will work from hard coded list of server names, for testing and safety sake)
;    Could load/run Novell's CONFIG.NLM on each server in the list (code for this exists below
;    but is commented out) but aren't, as CONFIG.NLM appears to be being run at least weekly
;    on our Netware 5/6 servers (by what?)
;    So, what this program currently does is....
;    collect and combine all the server configuration report files (CONFIG.TXT, plain text)
;    (both the NLM and TXT file are in each server's SYS:SYSTEM volume/directory)
;    and combine/save them out as a single compressed (zip format) file
;    to facilitate storage/e-mail off-site for routine server analysis/documentation
;    and disaster recovery.
;
;  output file location/name:
;    Currently, location is specified in FilePathDefaultValue
;    and file name is 
;      "NDS Servers Configurations %CurrentTimeYmdHmsWithBlanks%.zip" 
;=============================================================================================


;================================ SET DEFAULT VALUES HERE ====================================
; Default Values 
;NDSTree = '\\ACME_TREE\'
;UNC = '.ACME'                                      ; command line argument #1 (param1)
LocalTempDirectory = "c:\winnt\temp"					; for incrementally building the zip file
;FilePathDefaultValue = 'F:\Winbatch\'              ; for testing
FilePathDefaultValue = '\\R999_server\V999\groups\Isd_techservices\WinBatch\Reports\'
DialogTitle = 'NDS Servers Configurations'
;SpreadsheetDelimiter = @TAB
MappedDrive = 'L:'
; Manually built server list (for testing and safety purposes, until/unless get comfortable
; with letting this program loose on all servers.... 
; if were to loose on all servers, would need an exclusion table for specific servers
; to be excluded (example: CD-ROM server that is not really a Netware server), 
; plus generic/wildcard exclusions for virtual servers associated
; with cluster(?) and/or storage array network(SAN)(?))
; (note: all of the ServerList = StrCat.... lines should have @TAB delimiter at their end, 
;        except for the last one.... (it servers as the list delimiter character))
ServerList = ""
ServerList = StrCat(ServerList, ".XYZISD3.Email.ACME", @TAB)
ServerList = StrCat(ServerList, ".S_XYZ_00.ISD.ADM.ACME", @TAB)
ServerList = StrCat(ServerList, ".S_FLD_01.FLD.REG.ACME")
; remember, no @TAB on end of last server name above
;DiagnosticLine = StrCat("ServerList = ", ServerList)
;Message(DialogTitle, DiagnosticLine)

;=============================================================================================


;===================================== HOUSEKEEPING  =========================================
; tolerate errors.... necessary for NDS processing?
; (could turn this on and off more selectively, as needed.....)
ErrorMode(@OFF)
; load WinBatch's NetwareX extension
AddExtender('WWNWX34I.DLL')
; load WinBatch's Zip Extender extension
AddExtender("WWZIP34I.DLL")
; tell NetwareX to return typeless names from NDS
nwSetOptions(1, @TRUE)
; make user defined functions (UDFs below) defined/known to WinBatch interpreter
; (otherwise, must place them here, above/ahead of the code that calls/invokes them)
Gosub DefineUDFs  											; interpreter scans UDFs then returns here
;																	; so that UDFs can be placed below main code
;=============================================================================================



;============================== MAIN PROGRAM LOGIC STARTS HERE ===============================

; open message box and report program start
MessageLine = "Running..."
BoxOpen(DialogTitle, MessageLine)

; assure valid number of command line arguments (0 for interactive, 1 for batch/command line)
If ((param0 < 0) || (param0 > 1))
	; invalid number, report and wait for user acknowledgement and terminate the program
	MessageLine = StrCat("ERROR: Invalid number of command line arguments = ", param0)
	Pause(DialogTitle, MessageLine)
	Exit
Endif

; get valid base object specification for NDS search filter from user or command line
; (NDS tree plus UNC (.context or .server.context))
;BaseObjectSpec = udfGetValidUNC(NDSTree, UNC, DialogTitle, param0)
; NOTE: at this point BaseObjectSpec has the tree name prefixed on it

; get list of server(s) within/that satisfy base object specification
; use what was entered as base object specification for search of NDS for servers
; (which may return zero, one or more servers)
; (server object name(s) in list will be typless, distinguished, tab delimited, unsorted)
; get the list of servers
;ServerList = udfGetServerList(BaseObjectSpec)
; (note that server names in list do not have NDS tree name prefix....
;  as are not returned by nwSearchObjects......
;  we'll re-prefix later when mapping drives)
; diagnostic, if needed
; INSTEAD, for testing and safety sake, until sure we want to turn this loose on all servers,
; let's employ a manually built a list of servers  (see top of program)
; NOTE: Do not concatenate @TAB on end of last server name.

; remove servers from the list that should not be processed (i.e. virtual servers, etc.)
; ............

; sort the server list
ServerList = ItemSort(ServerList, @TAB)
;DiagnosticLine = StrCat("sorted ServerList = ", ServerList)
;Message(DialogTitle, DiagnosticLine)

; generate output zip (compressed) file name
CurrentTimeYmdHmsWithBlanks = StrReplace(TimeYmdHms(), ":", " ")
OutputZipFileName = StrCat(DialogTitle, " On ", CurrentTimeYmdHmsWithBlanks, ".zip") 

; capture and display server list processing start time
ServerListStartTimeYmdHms = TimeYmdHms()
MessageLine = StrCat(MessageLine, @CR, "Server List Processing Start Time ", ServerListStartTimeYmdHms)
BoxText(MessageLine)

; get count of servers in the list 
ServerCount = ItemCount(ServerList, @TAB)
MessageLine = StrCat(MessageLine, @CR, "Number Of Servers To Be Processed ", ServerCount)

; process servers in the list    (note: list starts at 1 (not at 0 like arrays do))
For ServerIndex = 1 to ServerCount by 1
	;ServerStartTimeYmdHms = TimeYmdHms()
	ServerName = ItemExtract(ServerIndex, ServerList, @TAB)
	ProgressLine = StrCat(MessageLine, @CR, "Processing server ", ServerIndex, " of ", ServerCount, "    ", ServerName)
	BoxText(ProgressLine)
	;DiagnosticLine = StrCat("ServerList[", ServerIndex, "] = ", ServerName)
	;Message(DialogTitle, DiagnosticLine)
	;ServerSpec = StrCat(NDSTree, ServerName)
	;NLMName = "CONFIG.NLM"
	; NOTE: FOLLOWING "RUN CONFIG.NLM" CODE COMMENTED OUT, AS IT IS ALREADY BEING RUN
	;       BY ?????? ON OUR NETWARE 5/6 SERVERS AT LEAST WEEKLY
	; run CONFIG.NLM (unless is already running, in which (unlikely) case we don't need to)
	;If (nwGetNLMInfo(ServerSpec, 17, NLMName) == "")
	;	; CONFIG.NLM is not already running (otherwise, would have returned an integer NLM ID)
	;	; so go ahead and load it
	;	NLMStartTimeYmdHms = TimeYmdHms()
	;	Result = nwSrvNLMMgr(ServerSpec, 1, NLMName)
	;	;ResultCode = LastError()
	;	;If ((Result != @TRUE) || (ResultCode != 0))
	;	If (Result != @TRUE)
	;		; NLM failed to load
	;		DiagnosticLine = StrCat("CONFIG.NLM load failed for ", ServerName)
	;		DiagnosticLine = StrCat(DiagnosticLine, " Result = ", Result)
	;		DiagnosticLine = StrCat(DiagnosticLine, " ResultCode = ", ResultCode)
	;		Message(DialogTitle, DiagnosticLine)
	;		; give up on this server and go back to top of for loop and process the next server
	;		Continue
	;	Else
	;		; NLM loaded
	;		; wait for it to finish, using a loop and delays measured in seconds
	;		TimeDelay(5)
	;		NLMIDNumber = nwGetNLMInfo(ServerSpec, 17, NLMName)
	;		;DiagnosticLine = StrCat("NLMIDNumber = ", NLMIDNumber, " for ", NLMName, " on ", ServerSpec)
	;		;Message(DialogTitle, DiagnosticLine)
	;		;While (IsNumber(nwGetNLMInfo(ServerSpec, 17, NLMName))
	;		While (IsNumber(NLMIDNumber))
	;			TimeDelay(5)
 	;			NLMIDNumber = nwGetNLMInfo(ServerSpec, 17, NLMName)
	;			;DiagnosticLine = StrCat("NLMIDNumber = ", NLMIDNumber, " for ", NLMName, " on ", ServerSpec)
	;			;Message(DialogTitle, DiagnosticLine)
	;		EndWhile 
			; append this server's SYS:\SYSTEM\CONFIG.TXT file into the output zip file by
			; generating \\servername\vol\path\ to server's SYS:SYSTEM\ location of the CONFIG.TXT file
			ServerSysSystemFilenameUNC = StrCat("\\", ItemExtract(2,ServerName,"."), "\sys\system\")
			; and map rooting a drive letter to that location (for use by FileCopy command)
			nwMap(ServerSysSystemFilenameUNC, MappedDrive, 0) 
			; diagnostic line to verify getting to server's sys:system
			;dir1=AskDirectory(MappedDrive, "L:", "","",0)
			; and generating a full drive:filename sourcename for use by the FileCopy
			SourceDriveFileName = StrCat(MappedDrive, "config.txt")
			; and generating a full drive:path\filename targetname for use by the FileCopy
			TargetDrivePathFileName = StrCat(LocalTempDirectory, "\", ItemExtract(2,ServerName,"."), ".txt")
			; and simple filename targetname for use by the zZipFiles
			TargetFileName = StrCat(ItemExtract(2,ServerName,"."), ".txt")
			; and copying it a local location, with a unique file name)
			FileCopy(SourceDriveFileName, TargetDrivePathFileName, @FALSE)
			; should error check the filecopy?
			; append the copied file into output zip file (which will be created for first server)
			origdir = DirGet()
			DirChange(LocalTempDirectory)
			;ZippedFilesList = zZipFiles("j", OutputZipFileName, TargetFileName, "")
			zZipFiles("j", OutputZipFileName, TargetFileName, "")
			; delete the temporary local copy of the server config file, now that it is in the zip file
			FileDelete(TargetFileName)
			DirChange(origdir)
			; should error check the zZipFiles?
			; NOTE: FOLLOWING NLMENDTIME COMMENTED OUT AS ARE NOT RUNNING CONFIG.NLM
			;NLMEndTimeYmdHms = TimeYmdHms()
			;NLMElapsedTimeMinutes = TimeDiffSecs(NLMEndTimeYmdHms, NLMStartTimeYmdHms) / 60
			;DiagnosticLine = StrCat("NLMElapsedTimeMinutes for ", ServerName, " = ", NLMElapsedTimeMinutes)
			;Message(DialogTitle, DiagnosticLine)
		;Endif      ; commented out as not running/checking CONFIG.NLM on servers (see above)
	;Endif      ; commented out as not running/checking CONFIG.NLM on servers (see above)
	;ServerEndTimeYmdHms = TimeYmdHms()
	;ServerElapsedTimeMinutes = TimeDiffSecs(ServerEndTimeYmdHms, ServerStartTimeYmdHms) / 60
	;DiagnosticLine = StrCat("ServerElapsedTimeMinutes for ", ServerName, " = ", ServerElapsedTimeMinutes)
	;Message(DialogTitle, DiagnosticLine)
Next

; all servers' config files are now consolidated in the zip file
; copy the zip file from c:\winnt\temp to located specified in FilePathDefaultValue
origdir = DirGet()
DirChange(LocalTempDirectory)
FileCopy(OutputZipFileName, FilePathDefaultValue, @FALSE)
FileDelete(OutputZipFileName)
DirChange(origdir) 

; capture server list processing end time
ServerListEndTimeYmdHms = TimeYmdHms()
MessageLine = StrCat(MessageLine, @CR, "Server List Processing End Time   ", ServerListStartTimeYmdHms)

; determine elapsed server list processing time
ServerListElapsedTimeMinutes = Int((TimeDiffSecs(ServerListEndTimeYmdHms, ServerListStartTimeYmdHms) / 60) + 1.0)
MessageLine = StrCat(MessageLine, @CR, "Server List Processing Elapsed Time Of  ", ServerListElapsedTimeMinutes, "  Minutes")
;DiagnosticLine = StrCat("ServerListElapsedTimeMinutes = ", ServerListElapsedTimeMinutes)
;Message(DialogTitle, DiagnosticLine)

; close the message box
BoxShut()

; report the results, via message requiring user acknowledgement
If (param0 == 0)
	MessageLine = StrCat("DONE.", @CR)
	MessageLine = StrCat(MessageLine, "Processed  ", ServerCount, "  servers", @CR)
	MessageLine = StrCat(MessageLine, "Elapsed time in minutes  ", ServerListElapsedTimeMinutes, @CR)
	MessageLine = StrCat(MessageLine, "Output file is  ", FilePathDefaultValue, OutputZipFileName)
	Message(DialogTitle, MessageLine)
Endif

; end the program
Exit


;=============================== MAIN PROGRAM LOGIC ENDS HERE ================================


;============================ DEFINE USER DEFINED FUNCTIONS (UDFs) =================================
:DefineUDFs  ; label for interpreter's gosub fallthru of UDFs
;            ; so that UDFs can be placed below main code
;            ; (interpreter must apparently scan user defined 
;            ;  functions before they can be called..... doing this
;            ;  would not be necessary if user defined functions
;            ;  were placed ahead of/above main program logic
;            ;  as interpreter would then do a fall-thru scan
;            ;  before hitting and executing the main logic
;===================================================================================================
; note: Function variables are local, subroutine variables are global.
;       The user array allocated at the top of the program is global (address is passed, not contents) 
;        (must explicitely pass it, but don't have to return it.... contents persist across functions).
;
;---------------------------------------------------------------------------------------------------
;
#DefineFunction udfGetValidUNC(NDSTree, UNC, DialogTitle, param0)
; get valid base object specification for NDS search filter
; (NDS tree plus user supplied UNC (.context or .server.context))

; being run interactively or batch/command line ?
If (param0 == 0)
	; interactively (no command line arguments were supplied)
	UNCEntered = ""
	NDSTreeUNCEntered = ""
	ResultCode = 1
	; loop till get valid base object specification from user 
	While ((ResultCode != 0) || (IsNumber(UNCEntered)) || (StrSub(UNCEntered, 1, 1) != "."))
		MessageLine = "NDS .context or .server.context "
		MessageLine = StrCat(MessageLine, "within ", NDSTree, " ?")
		;MessageLine = StrCat(MessageLine, @CR, "CAUTION: Multiple servers will be slow.")
		UNCEntered = AskLine(DialogTitle, MessageLine, UNC)
		; concatenate NDS tree plus user supplied UNC (.context or .server.context)
		NDSTreeUNCEntered = StrCat(NDSTree, UNCEntered)
		; verify that .context or .server.context portion of UNC supplied exists
		nwGetObjInfo(NDSTreeUNCEntered, 2)
		ResultCode = LastError()
		; determine if not found or if user entered grossly illegal UNC (all numeric or contains no period)
		If ((ResultCode != 0) || (IsNumber(UNCEntered)) || (StrSub(UNCEntered, 1, 1) != "."))
			MessageLine = "ERROR: NDS .context or .server.context"
			MessageLine = StrCat(MessageLine, "You entered = ", UNCEntered)
			Message(DialogTitle, MessageLine)
		Endif
	Endwhile
Else
	; batch/command line execution (not interactive)
	; UNC (.context or .server.context) supplied as first command line argument
	UNCEntered = param1
	; concatenate NDS tree plus supplied UNC (.context or .server.context)
	NDSTreeUNCEntered = StrCat(NDSTree, UNCEntered)
	; verify that .context or .server.context portion of UNC supplied exists
	nwGetObjInfo(NDSTreeUNCEntered, 2)
	ResultCode = LastError()
	; determine if not found or if user entered grossly illegal UNC (all numeric or contains no period)
	If ((ResultCode != 0) || (IsNumber(UNCEntered)) || (StrSub(UNCEntered, 1, 1) != "."))
		; for batch/command line run, should error message go to console or to report file?
		; for now, display it on the screen and wait for user acknowledgement and terminate the program
		MessageLine = "ERROR: NDS .context or .server.context  "
		MessageLine = StrCat(MessageLine, NDSTreeUNCEntered, " not found!  Abending!")
		Pause(DialogTitle, MessageLine)
		; end the program
		Exit
	Endif
Endif
	
; return valid base object specification for NDS search
Return (NDSTreeUNCEntered)
#EndFunction
;
;----------------------------------------------------------------------------------------------------
;
#DefineFunction udfGetServerList(BaseObjectSpec)
; get (unsorted) list from NDS of server objects that satisfy the base object specification

; set up a search filter
; (is cumulative... use *FREE_BUFFER* to reset/deactivate)
; (source of attribute names was the NDS Snoop program....
;  see www.novell.com/coolsolutions/tools/1005.html)
; (another source of attribute names is the output displayed by the example
;  WinBatch program supplied in the nwGetObjValue writeup in the NetwareX help)
nwSearchFilter(BaseObjectSpec, 'BASECLS','',0,0,0)
nwSearchFilter(BaseObjectSpec, 'ANAME','NCP Server',0,0,0)
nwSearchFilter(BaseObjectSpec, 'END','',0,0,0)
; set up search flags (combine/add various bit flag values)
; value of 2 says search scope is base-object and all its subordinates
Flags = 2
; tell WinBatch's NetwareX extensions to return typeless names from NDS
nwSetOptions(1,@TRUE)
; do the search and get the result code
ServerListDistinguishedTabbed = nwSearchObjects(BaseObjectSpec, '', '', Flags, '')
ResultCode = LastError()
; verify that search was ok
If (ResultCode != 0)
	; diagnostic (if needed) report result code
	MessageLine = StrCat("Servers search failed with result code = ", ResultCode)
	;Message(DialogTitle, MessageLine)
	Message("NDS Servers Configurations", MessageLine)
	; End the program
	Exit
Endif
; reset/deactivate the search filter (don't need when done with nwSearchObjects)
nwSearchFilter('', '*FREE_BUFFER*', '', 0, 0, 0)
; diagnostic (if needed)
;Message("NDS Servers Configuration", ServerListDistinguishedTabbed)
; diagnostic program termination (if needed)
;exit
; return list of volume objects found
Return (ServerListDistinguishedTabbed)
#EndFunction
 


;
;===================================================================================================
Return        ; return from interpreter's gosub fallthru of UDFs
;             ; so that UDFs can be placed below main code
;===================================================================================================

Article ID:   W16057
File Created: 2004:03:30:15:42:38
Last Updated: 2004:03:30:15:42:38