DATA Step, Macro, Functions and more

Setting up a production, test and development environment

Reply
N/A
Posts: 0

Setting up a production, test and development environment

This is what I have done to create an automated environment for Production, Test and Development of SAS code -- primarily for batch processing. Believe it or not, it is simplistic in that we are not, as yet, using a formal source code control/management product for this stuff.

My perspective is that I have been a Capacity Planner for over 9 years, now, and did a form of Business analysis prior to that. I have been programming in SAS either continually, or off and on, since January of 1996. But, I by no means consider myself an expert at SAS, having only really gotten into serious macro coding the last year or so. My experience is primarily limited to Base SAS, SAS/Graph, SAS/Stats, SAS/ETS, SAS/Access, SAS/Connect and EG.

The current environment consists of PC SAS on our workstations + EG (actually a whole SAS suite of products that I have not been able to get into yet); SAS on a Windows Server farm and SAS on a mainframe.

On the remote Windows Server is a base share, let's call the server SASsrv1 and the base share BaseRP (rp = resource planning = capacity planning group).
\\SASsrv1\BaseRP has below it the following directories
Prod
Test
Dev

Below Dev are individual user directories, one for each SAS user: Tom, Dick, Harry, Moe, Larry, Curly, Shemp, Lucy, Ethel, Maria, etc.

Below these base locations are a standard set of directories:
Adhoc
Main
IncludeLib
MacLib
FormatLib
DataLibs

Adhoc contains manually run code
Main contains scheduled code
IncludeLib contains generic included SAS code
MacLib contains Macro source code and is in the SASAUTOS group set.
FormatLib holds our permanent shared format dataset(s)
DataLibs is a base directory for permanent SAS dataset libraries

Main has subirectories, one for each processing group, like WAN, MOM, Best1, Tavve, etc. These directories have the code specific to that groups processing, retrieving data, summaries and reporting.

On each PC is an autoexec.sas file that has only one line in it
[pre]
%include '\\SASsrv1\BaseRP\Dev\[user's name]\autoexec.sas';
[/pre]
Mine is \\SASsrv1\BaseRP\Dev\Chuck\autoexec.sas
This way everyone has identically the same autoexec.sas options on their local PC's so we don't have to worry about something special/different.
The remote autoexec.sas has a standard header, standard primary code set, and then stuff that may be custom for a particular user. We have this stored on the remote server so that it is readily accessible without having to log onto any particular person's PC.

By the way, we cannot log into the remote server via terminal services, for security reasons; only the sysadmin(s) can do that.

The goal is to write code under ...\Dev\user and then be able to simply move it into test and it work within the test environment, and then into production. We do not want to have to copy all of production into test nor each dev environment. We need to be able to use production code when testing a development module.

This is accomplished by a macro %ProdTestOrDev and some common code in each user's remote autoexec.sas file.
[/pre]
%include "\\SASsrv1\BaseRP\Prod\MacLIB\BaseRP.sas";
%global BaseRP env;
%let BaseRP = %BaseRP ;
%put RP NOTE: BaseRP = &BaseRP;
filename MacLIb ("&BaseRP\Dev\Chuck\MacLib", "&BaseRP\Test\MacLib", "&BaseRP\Prod\MacLib");
options sasautos=(MacLib SASAUTOS);

%DetermineLibDef;
[/pre]
The %BaseRP macro provides a single point of maintenance for storing the base server and share name, should it change, which it has/does.
%DetermineLibDef is a macro that is used to set our standard libary definitions, based on which environment the source code is being run from, and if the process is foreground, background, batch, Interactive SAS or EG.

Looking at %DetermineLibDef;
[pre]
%macro DetermineLibDef();
%put RP NOTE: inside macro DetermineLibDef();
%put . This macro determines which environment to pull LibraryDefinitions.sas from;

%global env;
%let env = %ProdTestOrDev ;
%put RP NOTE: env = &env;
...
%mend;
[/pre]
Now you finally see %ProdTestOrDev being called, for the first time.
[pre]
%macro ProdTestOrDev(name);
%put RP Note: inside macro ProdTestOrDev(name);
%put - parameter name = &name ;

%local dummy do_more;

%let do_more = 0;

%*--- if a legitimate environment is given, pass it back ---;

%if %length(&name) %then %do;
%let dummy = %lowcase(&name);
%if "&dummy" = "prod" %then Prod;
%else %do;
%if "&dummy" = "test" %then Test;
%else %do;
%if "%scan(&dummy,1,'\')" = "user" %then &dummy;
%else %let do_more = 1;
%end;
%end;
%end;
%else %let do_more = 1;

%if &do_more %then %do;

%if %symexist(environment) %then %do;
%put RP NOTE: environment = &environment;
%if %length(&environment) %then %do;
%let dummy = %lowcase(&environment);
%if "&environment" = "SASEG" %then %do;
%if 1=1 %then &environment ;
%return;
%end;
%if "&dummy" = "prod" %then %do;
%if 1=1 %then Prod ;
%return;
%end;
%if "&dummy" = "test" %then %do;
%if 1=1 %then Test ;
%return;
%end;
%if "%scan(&dummy,1,'\')" = "dev" %then %do;
%if 1=1 %then &environment ;
%return;
%end;
%end;
%end;

%*--- _CLIENTPROJECTNAME is created by SAS Enterprise Guide ---;

%if %symexist(_CLIENTPROJECTNAME) %then %do;
%put _CLIENTPROJECTNAME exists and = &_CLIENTPROJECTNAME ;
%put sysenv = &sysenv;
%if %index( &_CLIENTPROJECTNAME,\Prod\) %then Prod ;
%else %do;
%if %index( &_CLIENTPROJECTNAME,\Test\) %then Test ;
%else %do;
%if %index( &_CLIENTPROJECTNAME,\Dev\) %then %do;
%let i = %index( &_CLIENTPROJECTNAME, \Dev\ );
%let dummy = %substr(&_CLIENTPROJECTNAME,&i+5);
%let j = %index( &dummy, \ );
%let name = %substr( &dummy,1,&j-1);
%if 1=1 %then Dev\&name ;
%end;
%else %do;
%if %length(&name) > 0 %then Dev\&name ;
%else EGUNKNOWN ;
%end;
%end;
%end;
%end;

%*--- Apparently the process is not running inside of SAS Enterprise Guide ---;
%else %do;
%let dummy = &sysprocessname ;
%put SysProcessName = &dummy ;
%put SysProcName = &SysProcName ;
%put SysStartName = &SysStartName ;
%put SysUserID = &SysUserID ;

%let filrf=mydir;
%let rc=%sysfunc(filename(filrf,'.'));
%let did=%sysfunc(dopen(&filrf));
%let infocnt=%sysfunc(doptnum(&did));
%let opt=%sysfunc(doptname(&did,1));
%let info=%sysfunc(dinfo(&did,&opt));
%put Source code executing out of "&info" ;
%let rc=%sysfunc(dclose(&did));

%if %index(&dummy,\Prod\) | %index(&info,\Prod\) %then Prod;
%else %do;
%if %index(&dummy,\Test\) | %index(&info,\Test\) %then Test;
%else %do;
%if %index(&dummy,\Dev\) | %index(&info,\Dev\) %then %do;
%if %index( &info, \Dev\ ) %then %let dummy = &info ;
%let i = %index( &dummy, \Dev\ );
%let dummy = %substr(&dummy,&i+5);
%let j = %index( &dummy, \ );
%let name = %substr( &dummy,1,&j-1);
%if 1=1 %then Dev\&name ;
%end;
%else %do;
%if %length(&name) > 0 %then Dev\&name ;
%else UNKNOWN ;
%end;
%end;
%end;
%end;
%end;
%put RP Note: leaving ProdTestOrDev macro;

%mend;
[/pre]
This is still a work in progress, especially with regards to the EG stuff.

The next part of the %DetermineLibDef macro determines if the process is running in the foreground or background, etc. and if foreground, brings up a dialog box/window that allows the user to explicitly set which LibraryDefinitions.sas file to attempt to use.

Here is a sample of LibraryDefinitions.sas
[pre]
%include "\\SASsrv1\BaseRP\Prod\MacLIB\BaseRP.sas";
%global BaseRP env;
%let BaseRP = %BaseRP ;
%include "&BaseRP\Prod\MacLib\ProdTestOrDev.sas";

%define_filename(pinclude,"&BaseRP\PROD\IncludeLib");
%define_filename(maclibu, "&BaseRP\&env\maclib"); *---> maclibs;
%define_filename(maclib, "&BaseRP\PROD\maclib");

OPTIONS SOURCE NOSOURCE2 STIMER FULLSTIMER
NOMPRINT NOMLOGIC MRECALL MAUTOSOURCE MEXECNOTE MAUTOLOCDISPLAY
SASAUTOS=(MACLIBU MACLIB SASAUTOS mxgsrc)
ERRORS=2
FMTSEARCH=(usrfmts MXGFMTS EGTASK SASUSER)
FMTERR DATE DTRESET
OBS=MAX NOXWAIT nobyline replace compress=yes
comamid=TCP
yearcutoff=1960
;

%define_datalibs( minutes, "&BaseRP\&env\DataLibs\minutes", "&BaseRP\Prod\DataLibs\minutes" ); *---> summaries with intervals in minutes;
%define_datalibs( misc, "&BaseRP\&env\DataLibs\misc", "&BaseRP\Prod\DataLibs\misc" );
%define_datalibs( months, "&BaseRP\&env\datalibs\Months", "&BaseRP\Prod\datalibs\months" ); *---> summaries with a month interval;
[/pre]

So now, whenever I submit code on my PC, if it from my development location, it works, using development code first, and if not found, production code.
When the code is moved into production it automatically finds all the correct stuff.

This gives me a safe environment to develop in, move code into a testing bullpen for regression testing by others, and then to execute in production, without changing the underlying code.

There are additional features, but this is long enough, if not too long, so far.
Ask a Question
Discussion stats
  • 0 replies
  • 412 views
  • 0 likes
  • 1 in conversation