mirror of
https://github.com/sasjs/core.git
synced 2025-12-22 10:41:20 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b34322d94 | |||
| 8bb83deede | |||
| 79c81aa8a4 | |||
| bbbcf7d550 | |||
| 82184bc6be | |||
| efc731cfaa | |||
| da9a74ee14 |
445
all.sas
445
all.sas
@@ -229,6 +229,32 @@ options noquotelenmax;
|
|||||||
%mend;
|
%mend;
|
||||||
|
|
||||||
/** @endcond *//**
|
/** @endcond *//**
|
||||||
|
@file
|
||||||
|
@brief Checks whether a fileref exists
|
||||||
|
@details You can probably do without this macro as it is just a one liner.
|
||||||
|
Mainly it is here as a convenient way to remember the syntax!
|
||||||
|
|
||||||
|
@param fref the fileref to detect
|
||||||
|
|
||||||
|
@return output Returns 1 if found and 0 if not found. Note - it is possible
|
||||||
|
that the fileref is found, but the file does not (yet) exist. If you need
|
||||||
|
to test for this, you may as well use the fileref function directly.
|
||||||
|
|
||||||
|
@version 8
|
||||||
|
@author [Allan Bowe](https://www.linkedin.com/in/allanbowe/)
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mf_existfileref(fref
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%if %sysfunc(fileref(&fref))=0 %then %do;
|
||||||
|
1
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
0
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend;/**
|
||||||
@file
|
@file
|
||||||
@brief Checks if a variable exists in a data set.
|
@brief Checks if a variable exists in a data set.
|
||||||
@details Returns 0 if the variable does NOT exist, and return the position of
|
@details Returns 0 if the variable does NOT exist, and return the position of
|
||||||
@@ -577,11 +603,18 @@ options noquotelenmax;
|
|||||||
@brief Adds custom quotes / delimiters to a delimited string
|
@brief Adds custom quotes / delimiters to a delimited string
|
||||||
@details Can be used in open code, eg as follows:
|
@details Can be used in open code, eg as follows:
|
||||||
|
|
||||||
%put %mf_getquotedstr(blah blah blah);
|
%put %mf_getquotedstr(blah blah blah);
|
||||||
|
|
||||||
which returns:
|
which returns:
|
||||||
> 'blah','blah','blah'
|
> 'blah','blah','blah'
|
||||||
|
|
||||||
|
Alternatively:
|
||||||
|
|
||||||
|
%put %mf_getquotedstr(these words are double quoted,quote=D)
|
||||||
|
|
||||||
|
for:
|
||||||
|
> "these","words","are","double","quoted"
|
||||||
|
|
||||||
@param in_str the unquoted, spaced delimited string to transform
|
@param in_str the unquoted, spaced delimited string to transform
|
||||||
@param dlm= the delimeter to be applied to the output (default comma)
|
@param dlm= the delimeter to be applied to the output (default comma)
|
||||||
@param indlm= the delimeter used for the input (default is space)
|
@param indlm= the delimeter used for the input (default is space)
|
||||||
@@ -4754,6 +4787,97 @@ proc sql
|
|||||||
%mp_binarycopy(inloc="&inloc",outref=_webout)
|
%mp_binarycopy(inloc="&inloc",outref=_webout)
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
|
%mend;/**
|
||||||
|
@file
|
||||||
|
@brief Runs arbitrary code for a specified amount of time
|
||||||
|
@details Executes a series of procs and data steps to enable performance
|
||||||
|
testing of arbitrary jobs.
|
||||||
|
|
||||||
|
%mp_testjob(
|
||||||
|
duration=60*5
|
||||||
|
)
|
||||||
|
|
||||||
|
@param [in] duration= the time in seconds which the job should run for. Actual
|
||||||
|
time may vary, as the check is done in between steps. Default = 30 (seconds).
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getuniquelibref.sas
|
||||||
|
@li mf_getuniquename.sas
|
||||||
|
@li mf_mkdir.sas
|
||||||
|
|
||||||
|
@version 9.4
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_testjob(duration=30
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
%local lib dir ds1 ds2 ds3 start_tm i;
|
||||||
|
|
||||||
|
%let start_tm=%sysfunc(datetime());
|
||||||
|
%let duration=%sysevalf(&duration);
|
||||||
|
|
||||||
|
/* create a temporary library in WORK */
|
||||||
|
%let lib=%mf_getuniquelibref();
|
||||||
|
%let dir=%mf_getuniquename();
|
||||||
|
%mf_mkdir(%sysfunc(pathname(work))/&dir)
|
||||||
|
libname &lib "%sysfunc(pathname(work))/&dir";
|
||||||
|
|
||||||
|
/* loop through until time expires */
|
||||||
|
%let ds1=%mf_getuniquename();
|
||||||
|
%let ds2=%mf_getuniquename();
|
||||||
|
%let ds3=%mf_getuniquename();
|
||||||
|
%do i=0 %to 1;
|
||||||
|
|
||||||
|
/* create big dataset */
|
||||||
|
data &lib..&ds1(compress=no );
|
||||||
|
do x=1 to 1000000;
|
||||||
|
randnum0=ranuni(0)*3;
|
||||||
|
randnum1=ranuni(0)*2;
|
||||||
|
bigchar=repeat('A',300);
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
%if %sysevalf( (%sysfunc(datetime())-&start_tm)>&duration ) %then %goto gate;
|
||||||
|
|
||||||
|
proc summary ;
|
||||||
|
class randnum0 randnum1;
|
||||||
|
output out=&lib..&ds2;
|
||||||
|
run;quit;
|
||||||
|
%if %sysevalf( (%sysfunc(datetime())-&start_tm)>&duration ) %then %goto gate;
|
||||||
|
|
||||||
|
/* add more data */
|
||||||
|
proc sql;
|
||||||
|
create table &lib..&ds3 as
|
||||||
|
select *, ranuni(0)*10 as randnum2
|
||||||
|
from &lib..&ds1
|
||||||
|
order by randnum1;
|
||||||
|
quit;
|
||||||
|
%if %sysevalf( (%sysfunc(datetime())-&start_tm)>&duration ) %then %goto gate;
|
||||||
|
|
||||||
|
proc sort data=&lib..&ds3;
|
||||||
|
by descending x;
|
||||||
|
run;
|
||||||
|
%if %sysevalf( (%sysfunc(datetime())-&start_tm)>&duration ) %then %goto gate;
|
||||||
|
|
||||||
|
/* wait 5 seconds */
|
||||||
|
data _null_;
|
||||||
|
call sleep(5,1);
|
||||||
|
run;
|
||||||
|
%if %sysevalf( (%sysfunc(datetime())-&start_tm)>&duration ) %then %goto gate;
|
||||||
|
|
||||||
|
%let i=0;
|
||||||
|
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%gate:
|
||||||
|
%put time is up!;
|
||||||
|
proc datasets lib=&lib kill;
|
||||||
|
run;
|
||||||
|
quit;
|
||||||
|
libname &lib clear;
|
||||||
|
|
||||||
|
|
||||||
%mend;/**
|
%mend;/**
|
||||||
@file mp_testwritespeedlibrary.sas
|
@file mp_testwritespeedlibrary.sas
|
||||||
@brief Tests the write speed of a new table in a SAS library
|
@brief Tests the write speed of a new table in a SAS library
|
||||||
@@ -12363,10 +12487,10 @@ run;
|
|||||||
%let fname3=%mf_getuniquefileref();
|
%let fname3=%mf_getuniquefileref();
|
||||||
%let fpath1=%sysfunc(pathname(&fname1));
|
%let fpath1=%sysfunc(pathname(&fname1));
|
||||||
%let fpath2=%sysfunc(pathname(&fname2));
|
%let fpath2=%sysfunc(pathname(&fname2));
|
||||||
%let fpath3=%sysfunc(pathname(&fname2));
|
%let fpath3=%sysfunc(pathname(&fname3));
|
||||||
|
|
||||||
/* compile the lua JSON module */
|
/* compile the lua JSON module */
|
||||||
%ml_json()
|
%ml_json()
|
||||||
/* read using LUA - this allows the code to be of any length */
|
/* read using LUA - this allows the code to be of any length */
|
||||||
data _null_;
|
data _null_;
|
||||||
file "&fpath3..lua";
|
file "&fpath3..lua";
|
||||||
@@ -12396,7 +12520,275 @@ data _null_;
|
|||||||
run;
|
run;
|
||||||
filename &fname1 clear;
|
filename &fname1 clear;
|
||||||
filename &fname2 clear;
|
filename &fname2 clear;
|
||||||
|
filename &fname3 clear;
|
||||||
%mend;
|
%mend;
|
||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Extract the log from a completed SAS Viya Job
|
||||||
|
@details Extracts log from a Viya job and writes it out to a fileref
|
||||||
|
|
||||||
|
To query the job, you need the URI. Sample code for achieving this
|
||||||
|
is provided below.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
First, compile the macros:
|
||||||
|
|
||||||
|
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
|
%inc mc;
|
||||||
|
|
||||||
|
Next, create a job (in this case, a web service):
|
||||||
|
|
||||||
|
filename ft15f001 temp;
|
||||||
|
parmcards4;
|
||||||
|
data ;
|
||||||
|
rand=ranuni(0)*1000;
|
||||||
|
do x=1 to rand;
|
||||||
|
y=rand*4;
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
proc sort data=&syslast
|
||||||
|
by descending y;
|
||||||
|
run;
|
||||||
|
;;;;
|
||||||
|
%mv_createwebservice(path=/Public/temp,name=demo)
|
||||||
|
|
||||||
|
Execute it:
|
||||||
|
|
||||||
|
%mv_jobexecute(path=/Public/temp
|
||||||
|
,name=demo
|
||||||
|
,outds=work.info
|
||||||
|
)
|
||||||
|
|
||||||
|
Wait for it to finish, and grab the uri:
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
set work.info;
|
||||||
|
if method='GET' and rel='self';
|
||||||
|
call symputx('uri',uri);
|
||||||
|
run;
|
||||||
|
|
||||||
|
Finally, fetch the log:
|
||||||
|
|
||||||
|
%mv_getjoblog(uri=&uri,outref=mylog)
|
||||||
|
|
||||||
|
This macro is used by the mv_jobwaitfor macro, which is generally a more
|
||||||
|
convenient way to wait for the job to finish before fetching the log.
|
||||||
|
|
||||||
|
|
||||||
|
@param [in] access_token_var= The global macro variable to contain the access token
|
||||||
|
@param [in] mdebug= set to 1 to enable DEBUG messages
|
||||||
|
@param [in] grant_type= valid values:
|
||||||
|
@li password
|
||||||
|
@li authorization_code
|
||||||
|
@li detect - will check if access_token exists, if not will use sas_services if
|
||||||
|
a SASStudioV session else authorization_code. Default option.
|
||||||
|
@li sas_services - will use oauth_bearer=sas_services.
|
||||||
|
@param [in] uri= The uri of the running job for which to fetch the status,
|
||||||
|
in the format `/jobExecution/jobs/$UUID/state` (unquoted).
|
||||||
|
@param [out] outref= The output fileref to which to APPEND the log (is always
|
||||||
|
appended).
|
||||||
|
|
||||||
|
|
||||||
|
@version VIYA V.03.04
|
||||||
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_abort.sas
|
||||||
|
@li mf_getplatform.sas
|
||||||
|
@li mf_existfileref.sas
|
||||||
|
@li ml_json.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mv_getjoblog(uri=0,outref=0
|
||||||
|
,contextName=SAS Job Execution compute context
|
||||||
|
,access_token_var=ACCESS_TOKEN
|
||||||
|
,grant_type=sas_services
|
||||||
|
,mdebug=0
|
||||||
|
);
|
||||||
|
%local oauth_bearer;
|
||||||
|
%if &grant_type=detect %then %do;
|
||||||
|
%if %symexist(&access_token_var) %then %let grant_type=authorization_code;
|
||||||
|
%else %let grant_type=sas_services;
|
||||||
|
%end;
|
||||||
|
%if &grant_type=sas_services %then %do;
|
||||||
|
%let oauth_bearer=oauth_bearer=sas_services;
|
||||||
|
%let &access_token_var=;
|
||||||
|
%end;
|
||||||
|
%put &sysmacroname: grant_type=&grant_type;
|
||||||
|
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||||
|
and &grant_type ne sas_services
|
||||||
|
)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(Invalid value for grant_type: &grant_type)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* validation in datastep for better character safety */
|
||||||
|
%local errmsg errflg;
|
||||||
|
data _null_;
|
||||||
|
uri=symget('uri');
|
||||||
|
if length(uri)<12 then do;
|
||||||
|
call symputx('errflg',1);
|
||||||
|
call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l');
|
||||||
|
end;
|
||||||
|
if scan(uri,-1)='state' or scan(uri,1) ne 'jobExecution' then do;
|
||||||
|
call symputx('errflg',1);
|
||||||
|
call symputx('errmsg',
|
||||||
|
"URI should be in format /jobExecution/jobs/$$$$UUID$$$$"
|
||||||
|
!!" but is actually like: &uri",'l');
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_abort(iftrue=(&errflg=1)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(&errmsg)
|
||||||
|
)
|
||||||
|
|
||||||
|
%mp_abort(iftrue=(&outref=0)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(Output fileref should be provided)
|
||||||
|
)
|
||||||
|
|
||||||
|
%if %mf_existfileref(&outref) ne 1 %then %do;
|
||||||
|
filename &outref temp;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
options noquotelenmax;
|
||||||
|
%local base_uri; /* location of rest apis */
|
||||||
|
%let base_uri=%mf_getplatform(VIYARESTAPI);
|
||||||
|
|
||||||
|
/* prepare request*/
|
||||||
|
%local fname1;
|
||||||
|
%let fname1=%mf_getuniquefileref();
|
||||||
|
proc http method='GET' out=&fname1 &oauth_bearer
|
||||||
|
url="&base_uri&uri";
|
||||||
|
headers
|
||||||
|
%if &grant_type=authorization_code %then %do;
|
||||||
|
"Authorization"="Bearer &&&access_token_var"
|
||||||
|
%end;
|
||||||
|
;
|
||||||
|
run;
|
||||||
|
%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then
|
||||||
|
%do;
|
||||||
|
data _null_;infile &fname1;input;putlog _infile_;run;
|
||||||
|
%mp_abort(mac=&sysmacroname
|
||||||
|
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||||
|
)
|
||||||
|
%end;
|
||||||
|
%local fname2 fname3 fpath1 fpath2 fpath3;
|
||||||
|
%let fname2=%mf_getuniquefileref();
|
||||||
|
%let fname3=%mf_getuniquefileref();
|
||||||
|
%let fpath1=%sysfunc(pathname(&fname1));
|
||||||
|
%let fpath2=%sysfunc(pathname(&fname2));
|
||||||
|
%let fpath3=%sysfunc(pathname(&fname3));
|
||||||
|
|
||||||
|
/* compile the lua JSON module */
|
||||||
|
%ml_json()
|
||||||
|
/* read using LUA - this allows the code to be of any length */
|
||||||
|
data _null_;
|
||||||
|
file "&fpath3..lua";
|
||||||
|
put '
|
||||||
|
infile = io.open (sas.symget("fpath1"), "r")
|
||||||
|
outfile = io.open (sas.symget("fpath2"), "w")
|
||||||
|
io.input(infile)
|
||||||
|
local resp=json.decode(io.read())
|
||||||
|
local logloc=resp["logLocation"]
|
||||||
|
outfile:write(logloc)
|
||||||
|
io.close(infile)
|
||||||
|
io.close(outfile)
|
||||||
|
';
|
||||||
|
run;
|
||||||
|
%inc "&fpath3..lua";
|
||||||
|
/* get log path*/
|
||||||
|
%let errflg=1;
|
||||||
|
%let errmsg=No entry in &fname2 fileref;
|
||||||
|
data _null_;
|
||||||
|
infile &fname2;
|
||||||
|
input;
|
||||||
|
uri=_infile_;
|
||||||
|
if length(uri)<12 then do;
|
||||||
|
call symputx('errflg',1);
|
||||||
|
call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l');
|
||||||
|
end;
|
||||||
|
if scan(uri,1) ne 'files' or scan(uri,2) ne 'files' then do;
|
||||||
|
call symputx('errflg',1);
|
||||||
|
call symputx('errmsg',
|
||||||
|
"URI should be in format /files/files/$$$$UUID$$$$"
|
||||||
|
!!" but is actually like: &uri",'l');
|
||||||
|
end;
|
||||||
|
call symputx('errflg',0,'l');
|
||||||
|
call symputx('logloc',uri,'l');
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_abort(iftrue=(&errflg=1)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(&errmsg)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* we have a log uri - now fetch the log */
|
||||||
|
proc http method='GET' out=&fname1 &oauth_bearer
|
||||||
|
url="&base_uri&logloc/content";
|
||||||
|
headers
|
||||||
|
%if &grant_type=authorization_code %then %do;
|
||||||
|
"Authorization"="Bearer &&&access_token_var"
|
||||||
|
%end;
|
||||||
|
;
|
||||||
|
run;
|
||||||
|
%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then
|
||||||
|
%do;
|
||||||
|
data _null_;infile &fname1;input;putlog _infile_;run;
|
||||||
|
%mp_abort(mac=&sysmacroname
|
||||||
|
,msg=%str(logfetch: &SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||||
|
)
|
||||||
|
%end;
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
file "&fpath3..lua";
|
||||||
|
put '
|
||||||
|
infile = io.open (sas.symget("fpath1"), "r")
|
||||||
|
outfile = io.open (sas.symget("fpath2"), "w")
|
||||||
|
io.input(infile)
|
||||||
|
local resp=json.decode(io.read())
|
||||||
|
for i, v in pairs(resp["items"]) do
|
||||||
|
outfile:write(v.line,"\n")
|
||||||
|
end
|
||||||
|
io.close(infile)
|
||||||
|
io.close(outfile)
|
||||||
|
';
|
||||||
|
run;
|
||||||
|
%inc "&fpath3..lua";
|
||||||
|
|
||||||
|
/* write log out to the specified fileref */
|
||||||
|
data _null_;
|
||||||
|
infile &fname2 end=last;
|
||||||
|
file &outref mod;
|
||||||
|
if _n_=1 then do;
|
||||||
|
put "/** SASJS Viya Job Log Extract start: &uri **/";
|
||||||
|
end;
|
||||||
|
input;
|
||||||
|
put _infile_;
|
||||||
|
%if &mdebug=0 %then %do;
|
||||||
|
putlog _infile_;
|
||||||
|
%end;
|
||||||
|
if last then do;
|
||||||
|
put "/** SASJS Viya Job Log Extract end: &uri **/";
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%if &mdebug=0 %then %do;
|
||||||
|
filename &fname1 clear;
|
||||||
|
filename &fname2 clear;
|
||||||
|
filename &fname3 clear;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
%put _local_;
|
||||||
|
%end;
|
||||||
|
%mend;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@file
|
@file
|
||||||
@brief Extract the status from a running SAS Viya job
|
@brief Extract the status from a running SAS Viya job
|
||||||
@@ -12986,21 +13378,30 @@ libname &libref;
|
|||||||
|
|
||||||
## Input table (minimum variables needed)
|
## Input table (minimum variables needed)
|
||||||
|
|
||||||
| variable| description |
|
@li FLOW_ID - Numeric value, provides sequential ordering capability
|
||||||
|---|---|---|
|
@li _CONTEXTNAME - Dictates which context should be used to run the job. If
|
||||||
|FLOW_ID| Numeric value, provides sequential ordering capability|
|
blank, will default to `SAS Job Execution compute context`.
|
||||||
|_CONTEXTNAME|Dictates which context should be used to run the job. If
|
@li _PROGRAM - Provides the path to the job itself
|
||||||
blank, will default to `SAS Job Execution compute context`.|
|
|
||||||
|_PROGRAM|Provides the path to the job itself|
|
Any additional variables provided in this table are converted into macro
|
||||||
|
variables and passed into the relevant job.
|
||||||
|
|
||||||
|
| FLOW_ID| _CONTEXTNAME |_PROGRAM|
|
||||||
|
|---|---|---|
|
||||||
|
|0|SAS Job Execution compute context|/Public/jobs/somejob1|
|
||||||
|
|0|SAS Job Execution compute context|/Public/jobs/somejob2|
|
||||||
|
|
||||||
## Output table (minimum variables produced)
|
## Output table (minimum variables produced)
|
||||||
|
|
||||||
| variable| description |
|
@li _PROGRAM - the SAS Drive path of the job
|
||||||
|---|---|---|
|
@li URI - the URI of the executed job
|
||||||
|FLOW_ID| Numeric value, provides sequential ordering capability|
|
@li STATE - the completed state of the job
|
||||||
|_CONTEXTNAME|Dictates which context should be used to run the job. If
|
@li TIMESTAMP - the datetime that the job completed
|
||||||
blank, will default to `SAS Job Execution compute context`.|
|
@li JOBPARAMS - the parameters that were passed to the job
|
||||||
|_PROGRAM|Provides the path to the job itself|
|
@li FLOW_ID - the id of the flow in which the job was executed
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
@@ -13342,7 +13743,7 @@ data;run;%let jdswaitfor=&syslast;
|
|||||||
should be in a `_program` variable.
|
should be in a `_program` variable.
|
||||||
@param [out] outds= The output dataset containing the list of states by job
|
@param [out] outds= The output dataset containing the list of states by job
|
||||||
(default=work.mv_jobexecute)
|
(default=work.mv_jobexecute)
|
||||||
|
@param [out] outref= A fileref to which the spawned job logs should be appended.
|
||||||
|
|
||||||
@version VIYA V.03.04
|
@version VIYA V.03.04
|
||||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
@@ -13353,6 +13754,7 @@ data;run;%let jdswaitfor=&syslast;
|
|||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
@li mf_existvar.sas
|
@li mf_existvar.sas
|
||||||
@li mf_nobs.sas
|
@li mf_nobs.sas
|
||||||
|
@li mv_getjoblog.sas
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
@@ -13361,6 +13763,7 @@ data;run;%let jdswaitfor=&syslast;
|
|||||||
,grant_type=sas_services
|
,grant_type=sas_services
|
||||||
,inds=0
|
,inds=0
|
||||||
,outds=work.mv_jobwaitfor
|
,outds=work.mv_jobwaitfor
|
||||||
|
,outref=0
|
||||||
);
|
);
|
||||||
%local oauth_bearer;
|
%local oauth_bearer;
|
||||||
%if &grant_type=detect %then %do;
|
%if &grant_type=detect %then %do;
|
||||||
@@ -13404,7 +13807,7 @@ options noquotelenmax;
|
|||||||
data _null_;
|
data _null_;
|
||||||
length jobparams $32767;
|
length jobparams $32767;
|
||||||
set &inds end=last;
|
set &inds end=last;
|
||||||
call symputx(cats('joburi',_n_),uri,'l');
|
call symputx(cats('joburi',_n_),substr(uri,1,55)!!'/state','l');
|
||||||
call symputx(cats('jobname',_n_),_program,'l');
|
call symputx(cats('jobname',_n_),_program,'l');
|
||||||
call symputx(cats('jobparams',_n_),jobparams,'l');
|
call symputx(cats('jobparams',_n_),jobparams,'l');
|
||||||
if last then call symputx('uricnt',_n_,'l');
|
if last then call symputx('uricnt',_n_,'l');
|
||||||
@@ -13448,14 +13851,20 @@ run;
|
|||||||
run;
|
run;
|
||||||
|
|
||||||
%if &status=completed or &status=failed or &status=canceled %then %do;
|
%if &status=completed or &status=failed or &status=canceled %then %do;
|
||||||
|
%local plainuri;
|
||||||
|
%let plainuri=%substr(&&joburi&i,1,55);
|
||||||
proc sql;
|
proc sql;
|
||||||
insert into &outds set
|
insert into &outds set
|
||||||
_program="&&jobname&i",
|
_program="&&jobname&i",
|
||||||
uri="&&joburi&i",
|
uri="&plainuri",
|
||||||
state="&status",
|
state="&status",
|
||||||
timestamp=datetime(),
|
timestamp=datetime(),
|
||||||
jobparams=symget("jobparams&i");
|
jobparams=symget("jobparams&i");
|
||||||
%let joburi&i=0; /* do not re-check */
|
%let joburi&i=0; /* do not re-check */
|
||||||
|
/* fetch log */
|
||||||
|
%if %str(&outref) ne 0 %then %do;
|
||||||
|
%mv_getjoblog(uri=&plainuri,outref=&outref)
|
||||||
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%else %if &status=idle or &status=pending or &status=running %then %do;
|
%else %if &status=idle or &status=pending or &status=running %then %do;
|
||||||
data _null_;
|
data _null_;
|
||||||
|
|||||||
27
base/mf_existfileref.sas
Normal file
27
base/mf_existfileref.sas
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Checks whether a fileref exists
|
||||||
|
@details You can probably do without this macro as it is just a one liner.
|
||||||
|
Mainly it is here as a convenient way to remember the syntax!
|
||||||
|
|
||||||
|
@param fref the fileref to detect
|
||||||
|
|
||||||
|
@return output Returns 1 if found and 0 if not found. Note - it is possible
|
||||||
|
that the fileref is found, but the file does not (yet) exist. If you need
|
||||||
|
to test for this, you may as well use the fileref function directly.
|
||||||
|
|
||||||
|
@version 8
|
||||||
|
@author [Allan Bowe](https://www.linkedin.com/in/allanbowe/)
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mf_existfileref(fref
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%if %sysfunc(fileref(&fref))=0 %then %do;
|
||||||
|
1
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
0
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend;
|
||||||
@@ -3,11 +3,18 @@
|
|||||||
@brief Adds custom quotes / delimiters to a delimited string
|
@brief Adds custom quotes / delimiters to a delimited string
|
||||||
@details Can be used in open code, eg as follows:
|
@details Can be used in open code, eg as follows:
|
||||||
|
|
||||||
%put %mf_getquotedstr(blah blah blah);
|
%put %mf_getquotedstr(blah blah blah);
|
||||||
|
|
||||||
which returns:
|
which returns:
|
||||||
> 'blah','blah','blah'
|
> 'blah','blah','blah'
|
||||||
|
|
||||||
|
Alternatively:
|
||||||
|
|
||||||
|
%put %mf_getquotedstr(these words are double quoted,quote=D)
|
||||||
|
|
||||||
|
for:
|
||||||
|
> "these","words","are","double","quoted"
|
||||||
|
|
||||||
@param in_str the unquoted, spaced delimited string to transform
|
@param in_str the unquoted, spaced delimited string to transform
|
||||||
@param dlm= the delimeter to be applied to the output (default comma)
|
@param dlm= the delimeter to be applied to the output (default comma)
|
||||||
@param indlm= the delimeter used for the input (default is space)
|
@param indlm= the delimeter used for the input (default is space)
|
||||||
|
|||||||
92
base/mp_testjob.sas
Normal file
92
base/mp_testjob.sas
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Runs arbitrary code for a specified amount of time
|
||||||
|
@details Executes a series of procs and data steps to enable performance
|
||||||
|
testing of arbitrary jobs.
|
||||||
|
|
||||||
|
%mp_testjob(
|
||||||
|
duration=60*5
|
||||||
|
)
|
||||||
|
|
||||||
|
@param [in] duration= the time in seconds which the job should run for. Actual
|
||||||
|
time may vary, as the check is done in between steps. Default = 30 (seconds).
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getuniquelibref.sas
|
||||||
|
@li mf_getuniquename.sas
|
||||||
|
@li mf_mkdir.sas
|
||||||
|
|
||||||
|
@version 9.4
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_testjob(duration=30
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
%local lib dir ds1 ds2 ds3 start_tm i;
|
||||||
|
|
||||||
|
%let start_tm=%sysfunc(datetime());
|
||||||
|
%let duration=%sysevalf(&duration);
|
||||||
|
|
||||||
|
/* create a temporary library in WORK */
|
||||||
|
%let lib=%mf_getuniquelibref();
|
||||||
|
%let dir=%mf_getuniquename();
|
||||||
|
%mf_mkdir(%sysfunc(pathname(work))/&dir)
|
||||||
|
libname &lib "%sysfunc(pathname(work))/&dir";
|
||||||
|
|
||||||
|
/* loop through until time expires */
|
||||||
|
%let ds1=%mf_getuniquename();
|
||||||
|
%let ds2=%mf_getuniquename();
|
||||||
|
%let ds3=%mf_getuniquename();
|
||||||
|
%do i=0 %to 1;
|
||||||
|
|
||||||
|
/* create big dataset */
|
||||||
|
data &lib..&ds1(compress=no );
|
||||||
|
do x=1 to 1000000;
|
||||||
|
randnum0=ranuni(0)*3;
|
||||||
|
randnum1=ranuni(0)*2;
|
||||||
|
bigchar=repeat('A',300);
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
%if %sysevalf( (%sysfunc(datetime())-&start_tm)>&duration ) %then %goto gate;
|
||||||
|
|
||||||
|
proc summary ;
|
||||||
|
class randnum0 randnum1;
|
||||||
|
output out=&lib..&ds2;
|
||||||
|
run;quit;
|
||||||
|
%if %sysevalf( (%sysfunc(datetime())-&start_tm)>&duration ) %then %goto gate;
|
||||||
|
|
||||||
|
/* add more data */
|
||||||
|
proc sql;
|
||||||
|
create table &lib..&ds3 as
|
||||||
|
select *, ranuni(0)*10 as randnum2
|
||||||
|
from &lib..&ds1
|
||||||
|
order by randnum1;
|
||||||
|
quit;
|
||||||
|
%if %sysevalf( (%sysfunc(datetime())-&start_tm)>&duration ) %then %goto gate;
|
||||||
|
|
||||||
|
proc sort data=&lib..&ds3;
|
||||||
|
by descending x;
|
||||||
|
run;
|
||||||
|
%if %sysevalf( (%sysfunc(datetime())-&start_tm)>&duration ) %then %goto gate;
|
||||||
|
|
||||||
|
/* wait 5 seconds */
|
||||||
|
data _null_;
|
||||||
|
call sleep(5,1);
|
||||||
|
run;
|
||||||
|
%if %sysevalf( (%sysfunc(datetime())-&start_tm)>&duration ) %then %goto gate;
|
||||||
|
|
||||||
|
%let i=0;
|
||||||
|
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%gate:
|
||||||
|
%put time is up!;
|
||||||
|
proc datasets lib=&lib kill;
|
||||||
|
run;
|
||||||
|
quit;
|
||||||
|
libname &lib clear;
|
||||||
|
|
||||||
|
|
||||||
|
%mend;
|
||||||
@@ -114,10 +114,10 @@ run;
|
|||||||
%let fname3=%mf_getuniquefileref();
|
%let fname3=%mf_getuniquefileref();
|
||||||
%let fpath1=%sysfunc(pathname(&fname1));
|
%let fpath1=%sysfunc(pathname(&fname1));
|
||||||
%let fpath2=%sysfunc(pathname(&fname2));
|
%let fpath2=%sysfunc(pathname(&fname2));
|
||||||
%let fpath3=%sysfunc(pathname(&fname2));
|
%let fpath3=%sysfunc(pathname(&fname3));
|
||||||
|
|
||||||
/* compile the lua JSON module */
|
/* compile the lua JSON module */
|
||||||
%ml_json()
|
%ml_json()
|
||||||
/* read using LUA - this allows the code to be of any length */
|
/* read using LUA - this allows the code to be of any length */
|
||||||
data _null_;
|
data _null_;
|
||||||
file "&fpath3..lua";
|
file "&fpath3..lua";
|
||||||
@@ -147,4 +147,5 @@ data _null_;
|
|||||||
run;
|
run;
|
||||||
filename &fname1 clear;
|
filename &fname1 clear;
|
||||||
filename &fname2 clear;
|
filename &fname2 clear;
|
||||||
|
filename &fname3 clear;
|
||||||
%mend;
|
%mend;
|
||||||
|
|||||||
267
viya/mv_getjoblog.sas
Normal file
267
viya/mv_getjoblog.sas
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Extract the log from a completed SAS Viya Job
|
||||||
|
@details Extracts log from a Viya job and writes it out to a fileref
|
||||||
|
|
||||||
|
To query the job, you need the URI. Sample code for achieving this
|
||||||
|
is provided below.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
First, compile the macros:
|
||||||
|
|
||||||
|
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
|
%inc mc;
|
||||||
|
|
||||||
|
Next, create a job (in this case, a web service):
|
||||||
|
|
||||||
|
filename ft15f001 temp;
|
||||||
|
parmcards4;
|
||||||
|
data ;
|
||||||
|
rand=ranuni(0)*1000;
|
||||||
|
do x=1 to rand;
|
||||||
|
y=rand*4;
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
proc sort data=&syslast
|
||||||
|
by descending y;
|
||||||
|
run;
|
||||||
|
;;;;
|
||||||
|
%mv_createwebservice(path=/Public/temp,name=demo)
|
||||||
|
|
||||||
|
Execute it:
|
||||||
|
|
||||||
|
%mv_jobexecute(path=/Public/temp
|
||||||
|
,name=demo
|
||||||
|
,outds=work.info
|
||||||
|
)
|
||||||
|
|
||||||
|
Wait for it to finish, and grab the uri:
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
set work.info;
|
||||||
|
if method='GET' and rel='self';
|
||||||
|
call symputx('uri',uri);
|
||||||
|
run;
|
||||||
|
|
||||||
|
Finally, fetch the log:
|
||||||
|
|
||||||
|
%mv_getjoblog(uri=&uri,outref=mylog)
|
||||||
|
|
||||||
|
This macro is used by the mv_jobwaitfor macro, which is generally a more
|
||||||
|
convenient way to wait for the job to finish before fetching the log.
|
||||||
|
|
||||||
|
|
||||||
|
@param [in] access_token_var= The global macro variable to contain the access token
|
||||||
|
@param [in] mdebug= set to 1 to enable DEBUG messages
|
||||||
|
@param [in] grant_type= valid values:
|
||||||
|
@li password
|
||||||
|
@li authorization_code
|
||||||
|
@li detect - will check if access_token exists, if not will use sas_services if
|
||||||
|
a SASStudioV session else authorization_code. Default option.
|
||||||
|
@li sas_services - will use oauth_bearer=sas_services.
|
||||||
|
@param [in] uri= The uri of the running job for which to fetch the status,
|
||||||
|
in the format `/jobExecution/jobs/$UUID/state` (unquoted).
|
||||||
|
@param [out] outref= The output fileref to which to APPEND the log (is always
|
||||||
|
appended).
|
||||||
|
|
||||||
|
|
||||||
|
@version VIYA V.03.04
|
||||||
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_abort.sas
|
||||||
|
@li mf_getplatform.sas
|
||||||
|
@li mf_existfileref.sas
|
||||||
|
@li ml_json.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mv_getjoblog(uri=0,outref=0
|
||||||
|
,contextName=SAS Job Execution compute context
|
||||||
|
,access_token_var=ACCESS_TOKEN
|
||||||
|
,grant_type=sas_services
|
||||||
|
,mdebug=0
|
||||||
|
);
|
||||||
|
%local oauth_bearer;
|
||||||
|
%if &grant_type=detect %then %do;
|
||||||
|
%if %symexist(&access_token_var) %then %let grant_type=authorization_code;
|
||||||
|
%else %let grant_type=sas_services;
|
||||||
|
%end;
|
||||||
|
%if &grant_type=sas_services %then %do;
|
||||||
|
%let oauth_bearer=oauth_bearer=sas_services;
|
||||||
|
%let &access_token_var=;
|
||||||
|
%end;
|
||||||
|
%put &sysmacroname: grant_type=&grant_type;
|
||||||
|
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||||
|
and &grant_type ne sas_services
|
||||||
|
)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(Invalid value for grant_type: &grant_type)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* validation in datastep for better character safety */
|
||||||
|
%local errmsg errflg;
|
||||||
|
data _null_;
|
||||||
|
uri=symget('uri');
|
||||||
|
if length(uri)<12 then do;
|
||||||
|
call symputx('errflg',1);
|
||||||
|
call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l');
|
||||||
|
end;
|
||||||
|
if scan(uri,-1)='state' or scan(uri,1) ne 'jobExecution' then do;
|
||||||
|
call symputx('errflg',1);
|
||||||
|
call symputx('errmsg',
|
||||||
|
"URI should be in format /jobExecution/jobs/$$$$UUID$$$$"
|
||||||
|
!!" but is actually like: &uri",'l');
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_abort(iftrue=(&errflg=1)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(&errmsg)
|
||||||
|
)
|
||||||
|
|
||||||
|
%mp_abort(iftrue=(&outref=0)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(Output fileref should be provided)
|
||||||
|
)
|
||||||
|
|
||||||
|
%if %mf_existfileref(&outref) ne 1 %then %do;
|
||||||
|
filename &outref temp;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
options noquotelenmax;
|
||||||
|
%local base_uri; /* location of rest apis */
|
||||||
|
%let base_uri=%mf_getplatform(VIYARESTAPI);
|
||||||
|
|
||||||
|
/* prepare request*/
|
||||||
|
%local fname1;
|
||||||
|
%let fname1=%mf_getuniquefileref();
|
||||||
|
proc http method='GET' out=&fname1 &oauth_bearer
|
||||||
|
url="&base_uri&uri";
|
||||||
|
headers
|
||||||
|
%if &grant_type=authorization_code %then %do;
|
||||||
|
"Authorization"="Bearer &&&access_token_var"
|
||||||
|
%end;
|
||||||
|
;
|
||||||
|
run;
|
||||||
|
%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then
|
||||||
|
%do;
|
||||||
|
data _null_;infile &fname1;input;putlog _infile_;run;
|
||||||
|
%mp_abort(mac=&sysmacroname
|
||||||
|
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||||
|
)
|
||||||
|
%end;
|
||||||
|
%local fname2 fname3 fpath1 fpath2 fpath3;
|
||||||
|
%let fname2=%mf_getuniquefileref();
|
||||||
|
%let fname3=%mf_getuniquefileref();
|
||||||
|
%let fpath1=%sysfunc(pathname(&fname1));
|
||||||
|
%let fpath2=%sysfunc(pathname(&fname2));
|
||||||
|
%let fpath3=%sysfunc(pathname(&fname3));
|
||||||
|
|
||||||
|
/* compile the lua JSON module */
|
||||||
|
%ml_json()
|
||||||
|
/* read using LUA - this allows the code to be of any length */
|
||||||
|
data _null_;
|
||||||
|
file "&fpath3..lua";
|
||||||
|
put '
|
||||||
|
infile = io.open (sas.symget("fpath1"), "r")
|
||||||
|
outfile = io.open (sas.symget("fpath2"), "w")
|
||||||
|
io.input(infile)
|
||||||
|
local resp=json.decode(io.read())
|
||||||
|
local logloc=resp["logLocation"]
|
||||||
|
outfile:write(logloc)
|
||||||
|
io.close(infile)
|
||||||
|
io.close(outfile)
|
||||||
|
';
|
||||||
|
run;
|
||||||
|
%inc "&fpath3..lua";
|
||||||
|
/* get log path*/
|
||||||
|
%let errflg=1;
|
||||||
|
%let errmsg=No entry in &fname2 fileref;
|
||||||
|
data _null_;
|
||||||
|
infile &fname2;
|
||||||
|
input;
|
||||||
|
uri=_infile_;
|
||||||
|
if length(uri)<12 then do;
|
||||||
|
call symputx('errflg',1);
|
||||||
|
call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l');
|
||||||
|
end;
|
||||||
|
if scan(uri,1) ne 'files' or scan(uri,2) ne 'files' then do;
|
||||||
|
call symputx('errflg',1);
|
||||||
|
call symputx('errmsg',
|
||||||
|
"URI should be in format /files/files/$$$$UUID$$$$"
|
||||||
|
!!" but is actually like: &uri",'l');
|
||||||
|
end;
|
||||||
|
call symputx('errflg',0,'l');
|
||||||
|
call symputx('logloc',uri,'l');
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_abort(iftrue=(&errflg=1)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(&errmsg)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* we have a log uri - now fetch the log */
|
||||||
|
proc http method='GET' out=&fname1 &oauth_bearer
|
||||||
|
url="&base_uri&logloc/content";
|
||||||
|
headers
|
||||||
|
%if &grant_type=authorization_code %then %do;
|
||||||
|
"Authorization"="Bearer &&&access_token_var"
|
||||||
|
%end;
|
||||||
|
;
|
||||||
|
run;
|
||||||
|
%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then
|
||||||
|
%do;
|
||||||
|
data _null_;infile &fname1;input;putlog _infile_;run;
|
||||||
|
%mp_abort(mac=&sysmacroname
|
||||||
|
,msg=%str(logfetch: &SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||||
|
)
|
||||||
|
%end;
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
file "&fpath3..lua";
|
||||||
|
put '
|
||||||
|
infile = io.open (sas.symget("fpath1"), "r")
|
||||||
|
outfile = io.open (sas.symget("fpath2"), "w")
|
||||||
|
io.input(infile)
|
||||||
|
local resp=json.decode(io.read())
|
||||||
|
for i, v in pairs(resp["items"]) do
|
||||||
|
outfile:write(v.line,"\n")
|
||||||
|
end
|
||||||
|
io.close(infile)
|
||||||
|
io.close(outfile)
|
||||||
|
';
|
||||||
|
run;
|
||||||
|
%inc "&fpath3..lua";
|
||||||
|
|
||||||
|
/* write log out to the specified fileref */
|
||||||
|
data _null_;
|
||||||
|
infile &fname2 end=last;
|
||||||
|
file &outref mod;
|
||||||
|
if _n_=1 then do;
|
||||||
|
put "/** SASJS Viya Job Log Extract start: &uri **/";
|
||||||
|
end;
|
||||||
|
input;
|
||||||
|
put _infile_;
|
||||||
|
%if &mdebug=0 %then %do;
|
||||||
|
putlog _infile_;
|
||||||
|
%end;
|
||||||
|
if last then do;
|
||||||
|
put "/** SASJS Viya Job Log Extract end: &uri **/";
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%if &mdebug=0 %then %do;
|
||||||
|
filename &fname1 clear;
|
||||||
|
filename &fname2 clear;
|
||||||
|
filename &fname3 clear;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
%put _local_;
|
||||||
|
%end;
|
||||||
|
%mend;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -9,21 +9,30 @@
|
|||||||
|
|
||||||
## Input table (minimum variables needed)
|
## Input table (minimum variables needed)
|
||||||
|
|
||||||
| variable| description |
|
@li FLOW_ID - Numeric value, provides sequential ordering capability
|
||||||
|---|---|---|
|
@li _CONTEXTNAME - Dictates which context should be used to run the job. If
|
||||||
|FLOW_ID| Numeric value, provides sequential ordering capability|
|
blank, will default to `SAS Job Execution compute context`.
|
||||||
|_CONTEXTNAME|Dictates which context should be used to run the job. If
|
@li _PROGRAM - Provides the path to the job itself
|
||||||
blank, will default to `SAS Job Execution compute context`.|
|
|
||||||
|_PROGRAM|Provides the path to the job itself|
|
Any additional variables provided in this table are converted into macro
|
||||||
|
variables and passed into the relevant job.
|
||||||
|
|
||||||
|
| FLOW_ID| _CONTEXTNAME |_PROGRAM|
|
||||||
|
|---|---|---|
|
||||||
|
|0|SAS Job Execution compute context|/Public/jobs/somejob1|
|
||||||
|
|0|SAS Job Execution compute context|/Public/jobs/somejob2|
|
||||||
|
|
||||||
## Output table (minimum variables produced)
|
## Output table (minimum variables produced)
|
||||||
|
|
||||||
| variable| description |
|
@li _PROGRAM - the SAS Drive path of the job
|
||||||
|---|---|---|
|
@li URI - the URI of the executed job
|
||||||
|FLOW_ID| Numeric value, provides sequential ordering capability|
|
@li STATE - the completed state of the job
|
||||||
|_CONTEXTNAME|Dictates which context should be used to run the job. If
|
@li TIMESTAMP - the datetime that the job completed
|
||||||
blank, will default to `SAS Job Execution compute context`.|
|
@li JOBPARAMS - the parameters that were passed to the job
|
||||||
|_PROGRAM|Provides the path to the job itself|
|
@li FLOW_ID - the id of the flow in which the job was executed
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@
|
|||||||
should be in a `_program` variable.
|
should be in a `_program` variable.
|
||||||
@param [out] outds= The output dataset containing the list of states by job
|
@param [out] outds= The output dataset containing the list of states by job
|
||||||
(default=work.mv_jobexecute)
|
(default=work.mv_jobexecute)
|
||||||
|
@param [out] outref= A fileref to which the spawned job logs should be appended.
|
||||||
|
|
||||||
@version VIYA V.03.04
|
@version VIYA V.03.04
|
||||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
@@ -83,6 +83,7 @@
|
|||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
@li mf_existvar.sas
|
@li mf_existvar.sas
|
||||||
@li mf_nobs.sas
|
@li mf_nobs.sas
|
||||||
|
@li mv_getjoblog.sas
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
@@ -91,6 +92,7 @@
|
|||||||
,grant_type=sas_services
|
,grant_type=sas_services
|
||||||
,inds=0
|
,inds=0
|
||||||
,outds=work.mv_jobwaitfor
|
,outds=work.mv_jobwaitfor
|
||||||
|
,outref=0
|
||||||
);
|
);
|
||||||
%local oauth_bearer;
|
%local oauth_bearer;
|
||||||
%if &grant_type=detect %then %do;
|
%if &grant_type=detect %then %do;
|
||||||
@@ -134,7 +136,7 @@ options noquotelenmax;
|
|||||||
data _null_;
|
data _null_;
|
||||||
length jobparams $32767;
|
length jobparams $32767;
|
||||||
set &inds end=last;
|
set &inds end=last;
|
||||||
call symputx(cats('joburi',_n_),uri,'l');
|
call symputx(cats('joburi',_n_),substr(uri,1,55)!!'/state','l');
|
||||||
call symputx(cats('jobname',_n_),_program,'l');
|
call symputx(cats('jobname',_n_),_program,'l');
|
||||||
call symputx(cats('jobparams',_n_),jobparams,'l');
|
call symputx(cats('jobparams',_n_),jobparams,'l');
|
||||||
if last then call symputx('uricnt',_n_,'l');
|
if last then call symputx('uricnt',_n_,'l');
|
||||||
@@ -178,14 +180,20 @@ run;
|
|||||||
run;
|
run;
|
||||||
|
|
||||||
%if &status=completed or &status=failed or &status=canceled %then %do;
|
%if &status=completed or &status=failed or &status=canceled %then %do;
|
||||||
|
%local plainuri;
|
||||||
|
%let plainuri=%substr(&&joburi&i,1,55);
|
||||||
proc sql;
|
proc sql;
|
||||||
insert into &outds set
|
insert into &outds set
|
||||||
_program="&&jobname&i",
|
_program="&&jobname&i",
|
||||||
uri="&&joburi&i",
|
uri="&plainuri",
|
||||||
state="&status",
|
state="&status",
|
||||||
timestamp=datetime(),
|
timestamp=datetime(),
|
||||||
jobparams=symget("jobparams&i");
|
jobparams=symget("jobparams&i");
|
||||||
%let joburi&i=0; /* do not re-check */
|
%let joburi&i=0; /* do not re-check */
|
||||||
|
/* fetch log */
|
||||||
|
%if %str(&outref) ne 0 %then %do;
|
||||||
|
%mv_getjoblog(uri=&plainuri,outref=&outref)
|
||||||
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%else %if &status=idle or &status=pending or &status=running %then %do;
|
%else %if &status=idle or &status=pending or &status=running %then %do;
|
||||||
data _null_;
|
data _null_;
|
||||||
|
|||||||
Reference in New Issue
Block a user