/* ** Copyright © 2007 SAS Institute Inc., ** SAS Campus Drive, Cary, North Carolina 27513, USA. All rights reserved. ** ** Refer to the Terms of Use and Legal Information on: http://www.sas.com/Copyright.html ** ** BBWin_WinServices_Config.sas ** Version 1.0 ** ** This SAS program will generate a VB script that will query the Windows defined services ** and pick out the SAS services that will be added to the BBWin configuration file ** for each host that is being monitored. ** ** You should execute this SAS program with SAS 9.1.3 on the Window's SAS mid-tier and ** backend machines that have running SAS services, and also on the Windows server that has the ** Grid Management Service defined and running. ** For the Grid Management, the program will follow the LSF environment variables and configuration ** files to determine the client machines and the services that are defined on those machines. ** ** In order for this program to query services, it will be utilizing the Windows WMI interface, and ** the executing userid on each of the servers should have administrators rights on the target machines ** to ensure the WMI commands (via a VB script) will successfully execute. ** ** Once the program has executed, you will find a subset of the BBWin.CFG file for each of the ** Windows servers. You should then include these entries into your current BBWIN.CFG ** files on each of those flagged machines. ** ** The only change necessary to invoke this program is to modify the ** bbwin_loc macro variable below to a directory location where you have write access to create ** these additional lines for the BBWIN.CFG files. */ /* Specify a directory where you want the definitions */ /* created so you can include them in your current BBWIN */ /* configuration file. */ %let bbwin_loc=C:\temp; /* <==== ONLY EDIT NEEDED. */ /*--------------------------------------------------------------------*/ /* DO NOT EDIT BELOW THIS LINE. */ /*--------------------------------------------------------------------*/ /*------------------ Declarations and Setup ------------------------*/ /* Some global macro variables that will be used in where clauses and */ /* will have some static values set. */ %global sasexe spawner connect lsfmgr lsfserv lsfsbd lsfres lsflim bbwin_loc thishost hostname; /* Here is a list of the executables that we will be looking through */ /* services that are known SAS services or one we rely on for Grid */ /* computing. */ %let sasexe=sas.exe; %let spawner=objspawn.exe; %let connect=spawner.exe; %let lsfmgr=jfd; %let lsfserv=gabd.exe; %let lsfsbd=sbatchd.exe; %let lsfres=res.exe; %let lsflim=lim.exe; /* Dynamically figure out the SAS WORK directory name for the running */ /* program so we can create a copy of the CSV file that is the */ /* output from the VB query script. */ proc sql noprint; select path into :PATHVAR from dictionary.members where libname='WORK'; /* We need this extra %trim to clean blanks off the dir name */ %let PATHVAR=%trim(&PATHVAR); quit; %put DEBUG: SAS WORK Location: &PATHVAR; /* DEBUG statement to see the WORK directory */ /* And we will pull in the hostname so we can use it in our filenames */ /* to make each of the generated files unique per host. */ data _null_; format host $20.; host=sysget("COMPUTERNAME"); call symput('THISHOST',trim(host)); run; %put DEBUG: Program executing on machine: &THISHOST; /*-------------------------------------------------------------------*/ /*------------------------ Start of Macros ------------------------*/ /*-------------------------------------------------------------------*/ /* This macro will dynamically build a VB script in the WORK */ /* directory location and then execute the script which will create */ /* a CSV file with all the services on a host. This CSV file will */ /* be imported, and then used to generate a separate CFG file for */ /* a passed in hostname. The VB script is utilizing the Windows */ /* WMI subsystem to query the services on a host. */ /* And in the instances of a LSF scheduler, this macro may be called */ /* repeatedly for each of the nodes in the grid. */ /*-------------------------------------------------------------------*/ %macro pull_services(hostname); /* Run an erase looking for previous CSV file so we can start fresh. */ %global cmd; data _null_; format cmdline $300.; cmdline="'" || 'erase "' || "&PATHVAR\service_list_&hostname..csv" || '"' || " '"; call symput('cmd',cmdline); put cmdline=; run; filename remove pipe &cmd; data _null_; infile remove; run; /* Let's build the VB file inline to be able to query the services */ /* and pull out the display name and the executable. */ data _null_; file "&PATHVAR\query.vbs" lrecl=200 pad; put 'Const ForAppending = 8'; put 'Set objFSO = CreateObject("Scripting.FileSystemObject")'; put 'Set objLogFile = objFSO.OpenTextFile("' "&PATHVAR\service_list_&hostname..csv" '", _'; put ' ForAppending, True)'; put 'objLogFile.Write _'; put ' ("System Name,Service Name,Service Type,Service State, Exit " _'; put ' & "Code,Process ID,Can Be Paused,Can Be Stopped,Caption," _'; put ' & "Description,Can Interact with Desktop,Display Name,Error " _'; put ' & "Control, Executable Path Name,Service Started," _'; put ' & "Start Mode,Account Name ")'; put 'objLogFile.Writeline'; /*put 'strComputer = "."';*/ put 'strComputer = "' "&hostname" '"'; put 'Set objWMIService = GetObject("winmgmts:" _'; put ' & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")'; put 'Set colListOfServices = objWMIService.ExecQuery _'; put ' ("Select * from Win32_Service")'; put ' For Each objService in colListOfServices'; put ' objLogFile.Write(objService.SystemName) & ","'; put ' objLogFile.Write(objService.Name) & ","'; put ' objLogFile.Write(objService.ServiceType) & ","'; put ' objLogFile.Write(objService.State) & ","'; put ' objLogFile.Write(objService.ExitCode) & ","'; put ' objLogFile.Write(objService.ProcessID) & ","'; put ' objLogFile.Write(objService.AcceptPause) & ","'; put ' objLogFile.Write(objService.AcceptStop) & ","'; put ' objLogFile.Write(objService.Caption) & ","'; put ' objLogFile.Write(objService.Description) & ","'; put ' objLogFile.Write(objService.DesktopInteract) & ","'; put ' objLogFile.Write(objService.DisplayName) & ","'; put ' objLogFile.Write(objService.ErrorControl) & ","'; put ' objLogFile.Write(objService.PathName) & ","'; put ' objLogFile.Write(objService.Started) & ","'; put ' objLogFile.Write(objService.StartMode) & ","'; put ' objLogFile.Write(objService.StartName) & ","'; put ' objLogFile.writeline'; put ' Next'; put ' objLogFile.Close'; run; /* Execute our generated VB script so we can have a CSV file to */ /* locate the defined services. */ /* Because of some quoting issues, we are going to build the command */ /* line in a macro variable and then include that macro variable. */ %global cmd; data _null_; format cmdline $300.; cmdline="'" || 'cscript "' || "&PATHVAR\query.vbs" || '"' || " '"; call symput('cmd',cmdline); put cmdline=; run; filename runvbs pipe &cmd; data _null_; infile runvbs; run; /* Now that we have a CSV file in the WORK location with the results */ /* we will import to have the data in a SAS dataset. */ proc import datafile="&PATHVAR\service_list_&hostname..csv" replace out=work.services; getnames=no; run; /* Quick renaming of the columns from the import to get something */ /* that is more meaningful. */ data services (keep=state processid services_name command_line); set services (rename=(var4=state var6=processid var9=services_name var14=command_line) ); run; /* Run through the list of ALL the services and pick out those that */ /* relevant for SAS and we will want to monitor with an IT package. */ proc sql; create table monitor_list as select * from services where command_line ? "&sasexe" or command_line ? "&spawner" or command_line ? "&connect" or command_line ? "&lsfmgr" or command_line ? "&lsfserv" or command_line ? "&lsfsbd" or command_line ? "&lsfres" or command_line ? "&lsflim"; quit; /*--------------- BBWIN specific CFG files ---------------------*/ /* Now that we have the services, we will build the necessary */ /* configuration lines for the IT monitoring package. */ /* This section is specifically built for a BBWIN stanzas format, */ /* but this block of code could be modified for other IT packages. */ /*-------------------------------------------------------------------*/ data _null_; file "&bbwin_loc\bbwin_sas_additions_&hostname..cfg" lrecl=120 pad; set monitor_list end=done; /* for the first data record, start the beginning of the stanza */ /* block */ if _n_=1 then do; put ''; put ''; end; /* and for the rest of the data, just write the setting line */ put ''; /* and at the end of the data, close out the stanza block */ if done then do; put ''; end; run; /* Done with the section of building BBWIN CFG files */ %mend; /* macro pull_services */ /*-------------------------------------------------------------------*/ /* To determine if this might be an LSF Grid Manager machine, we */ /* look at the Environment variable of LSF_ENVDIR, and then look at */ /* the lsf.conf file in there. This will have two parms we need */ /* LSF_CONFDIR path where we want to read the lsf.cluster.xxxx file */ /* and the xxxx value will come form the LSF_CLUSTER_ID in lsf.conf */ /* In the lsf.cluster.xxxx file, we can find the list of defined */ /* clients flagged by a "Begin Host" tag and then HOSTNAME lines */ /*-------------------------------------------------------------------*/ %macro lsf_drilldown; %global lsfdir lsfconf lsfcluster; /* First, we need the directory for the LSF_ENVDIR environment */ /* variable. */ data _null_; format lsf_dir $200.; lsf_dir=sysget("LSF_ENVDIR"); call symput('LSFDIR',trim(lsf_dir)); run; /* We have some data from LSF_ENVDIR, so let's continue the search */ /* because this looks like a Grid Manager server */ %if %length(&LSFDIR) > 0 %then %do; data _null_; infile "&LSFDIR\lsf.conf" pad missover lrecl=200; format aline $200. lsf_cluster_file $60. lsf_cluster $60. lsf_confdir $200.; input aline $1-200; /* Skip any comment lines */ if substr(aline,1,1) = "#" then delete; /* Search for the 2 lines we need to get more LSF client */ /* hostname information for services query. */ if index(aline,'LSF_CONFDIR') > 0 then do; put aline=; lsf_confdir=scan(aline,-1,'='); put lsf_confdir=; call symput('LSFCONF',trim(lsf_confdir)); end; if index(aline,'LSF_CLUSTER_ID') > 0 then do; put aline=; lsf_cluster=scan(aline,-1,'='); lsf_cluster_file='lsf.cluster.' || lsf_cluster; put lsf_cluster_file=; call symput('LSFCLUSTER',trim(lsf_cluster_file)); end; run; /* Finally, we want to read through the LSF.CLUSTER.xxxxx */ /* file that is defined in the LSF_CONFDIR directory. */ /* We will build a dataset with just the defined hosts */ %if %length(&LSFCLUSTER) > 0 %then %do; data just_hostnames; infile "&LSFCONF\&LSFCLUSTER" pad missover lrecl=200; format hostname $60. aline $200.; input aline $1-200; /* Skip any comment lines */ if substr(aline,1,1) = "#" or substr(aline,1,1) = "" then delete; /* Look for the Begin Host line to start the block of */ /* defined LSF client machines. */ /* Once we find the start, read the hostnames until we */ /* see the End Host line. */ if index(aline,'Begin') > 0 and index(aline,'Host') then do; end_found=0; do until (end_found=1); input aline $1-200; if substr(aline,1,1) = "#" then delete; hostname=scan(aline,1,''); /* also, drop any domain name off the hostname */ hostname=scan(hostname,1,'.'); if upcase(hostname) ^= 'HOSTNAME' then output; if index(aline,'End') > 0 and index(aline,'Host') then end_found=1; end; end; run; /* Before we query the client list for services, we will */ /* remove the hostname, if it exists, where we are */ /* executing this program. No need to requery the same */ /* machine twice. */ proc sql; delete * from just_hostnames where upcase(hostname) ? "&thishost"; quit; %end; /* We have something via the LSFCLUSTER */ /* Now, read through the list of client machines and find */ /* the list of possible running services so we can build a */ /* machine specific BBWIN.CFG services entry */ %let dsid = %sysfunc(open(work.just_hostnames)); %if &dsid %then %do; %syscall set(dsid); %let nobs =%sysfunc(attrn(&dsid,nlobs)); %do i=1 %to &nobs; %let rc=%sysfunc(fetch(&dsid)); %let one_machine=%sysfunc(getvarc(&dsid,%sysfunc(varnum(&dsid,HOSTNAME)))); %put Pulling services for &one_machine; %pull_services(&one_machine); %end; %let rc = %sysfunc(close(&dsid)); %end; /* looking for hostnames */ %end; /* checking on the LSFDIR existence */ %mend; /* macro lsf_drilldown */ /*------------- End of Macro Section ----------------------*/ /*-------------------- Main Body --------------------------*/ /* First, build the service list for the host that this is executing */ /* on. */ /* "thishost" is determined at the very top of the program where it */ /* does some initial setup and configuration. */ %pull_services(&thishost); /* Now, look at this host and see if it's the Grid Manager, if so */ /* we will look through the defined cluster file to get the client */ /* machines and try and pull together a list of services on each of */ /* those servers. */ /* This routine, if necessary, will then call the pull_services */ /* macro to build the cfg file(s). */ %lsf_drilldown; /* ** Copyright © 2007 SAS Institute Inc., ** SAS Campus Drive, Cary, North Carolina 27513, USA. All rights reserved. ** */