Hi All,
We are able to extract user, flowname, trigger,etc from below code.
We are looking to extract email notification (when flow exit or start, etc) with email address and options . see below screenshot . Found below info in .dat files but not sure how to extract them and also options.
17
FlowStateMailWhen
4
Exit
10
FlowStderr
0
15
MailDestination
24
email.com
10
OEMAppName
3
SAS
11
OutputFiles
options linesize=180;
ods graphics off;
ods html close;
ods listing;
%macro flowdef;
%let JSHOME=/sas/common/pm;
%PUT JSHOME = "%trim(&JSHOME)";
%if %length(%trim(&JSHOME)) = 0 %then
%do;
%put %str();
%put ERROR: JS_HOME environment variable not available, exiting;
data _null_; abort return 16; run;
%end;
%if &sysscp = WIN %then
%do;
filename events "%trim(&JSHOME)\work\system\triggerevents.dat";
filename flowdefs "%trim(&JSHOME)\work\storage\flow_storage\*.dat";
%end;
%else
%do;
filename events "/sas/common/pm/work/system/triggerevents.dat";
filename flowdefs "/sas/common/pm/work/storage/flow_storage/*.dat";
%end;
%mend;
%flowdef;
data triggers;
infile events;
length owner $ 16 flowname $ 32;
input; * version info;
input; * number of events;
nevents = input(_infile_||" ",5.); drop nevents;
do i=1 to nevents;
input; * length of flow name;
input; * flow name;
owner = scan(_infile_,1,":");
flowname = scan(_infile_,2,":");
output;
do until(_infile_ = "</List>");
input;
end;
end;
run;
filename events;
data scheduled adhoc;
length owner $ 16 flowname $ 32 event $ 14 jobname details $ 64 specifics $ 32 runas $ 32 eventtype $ 4 flowpart instance $ 8 removed $ 1 calendar calowner $ 32 timepart $ 65 nextrun $ 17 flowID $ 6;
retain owner flowname eventtype flowpart instance removed flowID;
drop calowner ;
infile flowdefs;
input;
if _infile_ = '<JFFlow>' then
do;
jobname = " "; runas = " "; nextrun = " "; EventType = " "; FlowPart = " "; flowID = " ";
/* Job File Flow definition found. Get FlowName, Owner, and Instance */
input; /* eat next line -- length */
input; /* should be username:flowname */
owner = scan(_infile_,1,':');
flowname = scan(_infile_,2,':');
input; /* eat next line -- length */
input; /* should be same as above with %%%%%## instance */
instance = scan(_infile_,2,'%');
input; /* eat next line -- length */
input; /* eat next line -- <List> token */
input; /* this is the number of Flow IDs that have run already */
nflows = input(_infile_||" ",3.);
do i=1 to nflows;
input;
end;
/* we are at the last run flow ID or the same 0 if none */
if nflows then
flowID = _infile_;
else
flowID = " ";
input; /* eat next line -- length */
input; /* eat next line -- </List> token */
input;
/* This line is questionable. Empirical observation had found that if the character is "F" then the flow has NOT */
/* been "removed," so is still scheduled. Otherwise, if it is "T" it HAS been "removed" and is an AdHoc flow. */
/* However, more recent complex flow instances have cast a shadow of doubt onto this notion. Stay tuned.... */
removed = _infile_;
/* Between the previous flow list and the next <List> are some things we can ignore (flow security, flow version) */
/* so just scan for the next List header -- this should be the list of flow triggers, file and/or time. */
do until (_infile_ = '<List>');
input; /* eat everything until list of flow triggers */
end;
input;
FlowPart = "Events";
ntriggers = input(_infile_||' ',5.);
* if ntriggers = 0 then output;
do i = 1 to ntriggers;
do until (_infile_ = "<JFCalendarEventDef>" or _infile_ = "<JFFileEventDef>");
input;
end;
if _infile_ = "<JFFileEventDef>" then EventType = "File"; else EventType = "Time";
do until (_infile_ = "</JFEventDef>");
input;
end;
input; input; /* eat the length of the next token and then get the next token */
if EventType = "File" then
do;
event = "File Trigger: ";
details = _infile_;
specifics = " ";
end;
else
if _infile_ = '<JFTimeInstance>' then
do;
/* Job File Trigger instance found. Get Calendar name and time */
input; /* eat next line -- length */
input; /* should be calendar:hh:mm%dur */
calendar = scan(_infile_,1,':');
timepart = scan(_infile_,2,':')||':'||scan(_infile_,3,':%');
input; /* eat next line -- length */
input; /* eat calendar name repeat */
input; /* eat next line -- length */
input; /* calendar owner */
calowner = tranwrd(_infile_,'DT_BS_','.\');
calendar = trim(calendar)||'@'||trim(calowner);
event = "Time Trigger: ";
details = trim(calendar) || " at " || trim(timepart);
end;
else
do;
put "ERROR: Invalid Flow Event Trigger type: " _infile_;
put _all_; abort return 5;
end;
do until (_infile_ = "</RuntimeClass>");
input;
end;
link out; /* output information on each flow's trigger */
end;
end;
else if _infile_ = '<SubFlowEvents>' then
do;
do until (_infile_ = '<JFFlow>');
input;
end;
/* Job File SUB Flow definition found. Get SubFlow Name */
input; /* eat next line -- length */
input; /* should be username:flowname:subflow */
details = scan(_infile_,3,':');
specifics = " ";
flowpart = "SubFlows";
event = "SubFlow : ";
link out;
/* Just eat everything until end of sub flow */
do until (_infile_ = '</SubFlowEvents>');
input;
end;
end;
else if _infile_ = '</JFFlow>' then
do;
jobname = " "; runas = " "; nextrun = " "; EventType = " ";
/* We are either at the end of a flow or at the end of all */
end;
else if _infile_ = '<JFJob>' then
do;
input; /* eat next line -- length */
input; /* eat next line -- "lsf" or "link" */
if _infile_ = "link" then
do until (_infile_ = '</JFJob>');
input;
end;
else
do;
FlowPart = "Jobs";
/* "lsf" Job Flow Job definition found */
input; /* eat next line -- length */
input; /* should be username:flowname:jobname{metaid} */
jobname = scan(_infile_,3,':{');
runas = owner;
do until (_infile_ = '</JFJob>');
input;
if _infile_ = 'SubmissionCmd' then
do;
input; /* eat next line -- length */
input; /* eat next line -- "bsub" */
input; /* eat next line -- length */
input; /* eat next line -- "UserName" */
input; /* eat next line -- length */
input; /* should be RunAs username */
runas = _infile_;
end;
end;
/* Write out every job within a flow */
event = "Job: ";
details = jobname;
specifics = " ";
if "&sysscp" = "WIN" or index(owner,"\") then
iffy = lowcase(runas) ^= lowcase(owner);
else
iffy = runas ^= owner;
if iffy then
specifics = "Run As: " || trim(runas);
link out;
end;
end;
return;
out:
if removed = "T" or ntriggers = 0 then output adhoc; else output scheduled;
return;
run;
filename flowdefs;
proc sort data=scheduled; by owner flowname descending instance flowpart; run;
data schedules;
set scheduled;
length firstinst $ 8;
retain firstinst;
by owner flowname descending instance flowpart;
if first.flowname then firstinst = instance;
if instance = firstinst;
drop firstinst;
run;
/* Now remove flows that are not REALLY "scheduled" -- not found in triggers file */
proc sort data=triggers; by owner flowname; run;
data schedules nonsched;
merge triggers(in=scheduled) schedules;
by owner flowname;
if scheduled then output schedules;
else output nonsched;
run;
proc sort data=schedules; by owner flowname flowID; run;
proc sql;
create table schedule as
select * from work.schedules
where event in ('Time Trigger:', 'File Trigger:')
;
quit;
proc sql;
create table active_schedule as
select owner, flowname, details as trigger_event, eventtype as trigger_type, flowid
from schedule
;
quit;
This sample code builds a number of tables about flows, jobs and data step batch servers. The last data step pulls the flow email configuration.
/******************************************************************************/
/* This program creates a set of tables from Metadata containing information */
/* about jobs and flows. */
/* These tables are: */
/* Flows - a tables of defined flows */
/* Flowsched - a table of scheduled flows and their schedules */
/* Flowjobs - a table of flow/job associations */
/* Flownoti - a table of flow email notification settings */
/* Jobs - a table of deployed jobs and their associated data servers */
/* dsbs - a table of data step batch servers and their attributes */
/* cmd - a table of any customized commands for jobs. */
/* Date: 09DEC2025 */
/******************************************************************************/
/* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. */
/* SPDX-License-Identifier: Apache-2.0 */
/* Provide connection to Metadata */
%let mduser=sasadm@saspw;
%let mdpass=password;
%let mdserver=meta.demo.sas.com;
%let mdport=8561;
/* Specify Data Step Batch Server values */
/* 9.4 Values */
%let dsbslogprop=BatchServer.DataStep.Property.LogDir.xmlKey.txt;
%let dsbslogext=BatchServer.DataStep.Property.LogExt.xmlKey.txt;
%let dsbscmdline=BatchServer.DataStep.Property.CmdLine.xmlKey.txt;
/* Set Metadata connection options based on macro variables set above. */
options metaserver="&mdserver"
metaport=&mdport
metaprotocol='bridge'
metauser="&mduser"
metapass="&mdpass"
metarepository='Foundation'
metaconnect='NONE'
;
/* Pull properties for the Data Step Batch Server(s) */
data dsbs (keep=dsbs_id dsbsname dsbscmdline dsbslogpath dsbslogext);
length type id prop_uri dsbs_uri $ 50 prop_name $ 255 propval dsbsname dsbscmdline dsbslogpath dsbslogext $ 1024 dsbs_id $ 17;
call missing (of _character_);
dsbsobj="omsobj:ServerComponent?@PublicType='Server.DataStepBatch'";
dsbscount=metadata_resolve(dsbsobj,type,id);
if dsbscount ge 1 then do j=1 to dsbscount;
rc=metadata_getnobj(dsbsobj,j,dsbs_uri);
rc=metadata_getattr(dsbs_uri,"Id",dsbs_id);
rc=metadata_getattr(dsbs_uri,"Name",dsbsname);
propcount=metadata_getnasn(dsbs_uri,"Properties",1,prop_uri);
if propcount > 0 then do i=1 to propcount;
rc=metadata_getnasn(dsbs_uri,"Properties",i,prop_uri);
rc=metadata_getattr(prop_uri,"Name",prop_name);
rc=metadata_getattr(prop_uri,"DefaultValue",propval);
if prop_name="&dsbscmdline" then do;
dsbscmdline=propval;
end;
else if prop_name="&dsbslogprop" then do;
dsbslogpath=propval;
end;
else if prop_name="&dsbslogext" then do;
dsbslogext=propval;
end;
end;
output;
end;
run;
/* Create table of flow IDs and schedule details */
data flowsched (keep=flow_id event_condition schedule);
length type id flow_uri step_uri event_uri prop_uri prop_name $ 50 flow_name $ 255 event_condition prop_val schedule $ 1024 flow_id $ 17;
call missing(of _character_);
flow_obj="omsobj:JFJob?@PublicType='DeployedFlow'";
flowcount=metadata_resolve(flow_obj,type,id);
put "NOTE: Found " flowcount "flows.";
if flowcount ge 1 then do i=1 to flowcount;
rc=metadata_getnobj(flow_obj,i,flow_uri);
rc=metadata_getattr(flow_uri,"Id",flow_id);
rc=metadata_getnasn(flow_uri,"Steps",1,step_uri);
rc=metadata_getnasn(step_uri,"TriggeringEvents",1,event_uri);
rc=metadata_getattr(event_uri,"Condition",event_condition);
propcount=metadata_getnasn(event_uri,"Properties",1,prop_uri);
if propcount ge 1 then do l=1 to propcount;
rc=metadata_getnasn(event_uri,"Properties",l,prop_uri);
rc=metadata_getattr(prop_uri,"Name",prop_name);
if prop_name = "Definition" then do;
rc=metadata_getattr(prop_uri,"DefaultValue",prop_val);
schedule=trim(prop_val);
output;
end;
end;
end;
run;
/* Create table of flow IDs and Names */
data flows (keep=flow_id flow_name);
length type id flow_uri $ 50 flow_name $ 255 flow_id $ 17;
call missing(of _character_);
flow_obj="omsobj:JFJob?@PublicType='DeployedFlow'";
flowcount=metadata_resolve(flow_obj,type,id);
put "NOTE: Found " flowcount "flows.";
if flowcount ge 1 then do i=1 to flowcount;
rc=metadata_getnobj(flow_obj,i,flow_uri);
rc=metadata_getattr(flow_uri,"Name",flow_name);
rc=metadata_getattr(flow_uri,"Id",flow_id);
output;
end;
run;
/* Create table of job details. */
data jobs (keep=job_name dir_name file_name dsbs_id job_id);
length type id job_uri file_uri dir_uri dsbs_uri $ 50 job_name dir_name file_name $ 255 dsbs_id job_id $ 17;
call missing(of _character_);
jobobj="omsobj:JFJob?@PublicType='DeployedJob'";
jobcount=metadata_resolve(jobobj,type,id);
if jobcount ge 1 then do i=1 to jobcount;
rc=metadata_getnobj(jobobj,i,job_uri);
rc=metadata_getattr(job_uri,"Name",job_name);
rc=metadata_getattr(job_uri,"Id",job_id);
rc=metadata_getnasn(job_uri,"SourceCode",1,file_uri);
rc=metadata_getattr(file_uri,"FileName",file_name);
rc=metadata_getnasn(file_uri,"Directories",1,dir_uri);
rc=metadata_getattr(dir_uri,"DirectoryName",dir_name);
rc=metadata_getnasn(job_uri,"TargetSpecifications",1,dsbs_uri);
rc=metadata_getattr(dsbs_uri,"Id",dsbs_id);
output;
end;
run;
/* Create table of custom command lines and their associated job. */
data cmd (keep=job_id cmd_val);
length type id cmd_uri step_uri job_uri $ 50 cmd_val $ 1024 job_id $ 17;
call missing (of _character_);
cmdobj="omsobj:Property?Property[@Name='CmdLine'][AssociatedObject/TransformationStep/Transformations/JFJob]";
cmdcount=metadata_resolve(cmdobj,type,id);
if cmdcount ge 1 then do i=1 to cmdcount;
rc=metadata_getnobj(cmdobj,i,cmd_uri);
rc=metadata_getattr(cmd_uri,"DefaultValue",cmd_val);
rc=metadata_getnasn(cmd_uri,"AssociatedObject",1,step_uri);
rc=metadata_getnasn(step_uri,"Transformations",1,job_uri);
rc=metadata_getattr(job_uri,"Id",job_id);
output;
end;
run;
/* Get Flow/Job associations */
data flowjobs (keep=flow_id job_id);
length type id flow_uri act_uri step_uri job_uri $ 50 flow_name $ 255 flow_id job_id $ 17;
call missing (of _character_);
flowobj="omsobj:JFJob?@PublicType='DeployedFlow'";
flowcount=metadata_resolve(flowobj,type,id);
if flowcount ge 1 then do i=1 to flowcount;
rc=metadata_getnobj(flowobj,i,flow_uri);
rc=metadata_getattr(flow_uri,"Id",flow_id);
actcount=metadata_getnasn(flow_uri,"JobActivities",1,step_uri);
if actcount ge 1 then do j=1 to actcount;
rc=metadata_getnasn(flow_uri,"JobActivities",j,act_uri);
stepcount=metadata_getnasn(act_uri,"Steps",1,step_uri);
if stepcount ge 1 then do k=1 to stepcount;
rc=metadata_getnasn(act_uri,"Steps",k,step_uri);
jobcount=metadata_getnasn(step_uri,"Transformations",1,job_uri);
if jobcount ge 1 then do l=1 to jobcount;
rc=metadata_getnasn(step_uri,"Transformations",l,job_uri);
rc=metadata_getattr(job_uri,"Id",job_id);
output;
end;
end;
end;
end;
run;
/* Extract sysin and log values from custom command. This is currently limited to UNIX-like paths. */
data cmd (keep=job_id cmd_val log sys);
set cmd;
if _N_=1 then do;
retain logpatternID syspatternID;
logpattern='/-log[[:space:]]+\/[A-z0-9\/\.]+/';
syspattern='/-sysin[[:space:]]+\/[A-z0-9\/\.]+/';
logpatternID=prxparse(logpattern);
syspatternID=prxparse(syspattern);
end;
call prxsubstr(logpatternID,cmd_val,position,length);
log=substr(cmd_val,position+5,length-5);
call prxsubstr(syspatternID,cmd_val,position,length);
sys=substr(cmd_val,position+7,length-7);
run;
/* Create a table of flow IDs and their email configurations */
data flownoti (keep=flow_id flow_notify job_notify flow_address job_address);
length type id flow_uri flow_id prop_uri prop_name $ 50 flow_notify job_notify $25 flow_address job_address $ 255;
call missing(of _character_);
obj="omsobj:JFJob?@PublicType='DeployedFlow'";
flowcount=metadata_resolve(obj,type,id);
put "NOTE: Found " flowcount "flows.";
if flowcount ge 1 then do i=1 to flowcount;
rc=metadata_getnobj(obj,i,flow_uri);
put "NOTE: Processing flow " flow_uri;
rc=metadata_getattr(flow_uri,"Id",flow_id);
propcount=metadata_getnasn(flow_uri,"Properties",1,prop_uri);
if propcount ge 1 then do j=1 to propcount;
rc=metadata_getnasn(flow_uri,"Properties",j,prop_uri);
rc=metadata_getattr(prop_uri,"Name",prop_name);
if prop_name = "EmailNotification" then do;
rc=metadata_getattr(prop_uri,"DefaultValue",flow_notify);
end;
else if prop_name = "EmailNotificationAddress" then do;
rc=metadata_getattr(prop_uri,"DefaultValue",flow_address);
end;
else if prop_name = "JobDefaultEmailNotification" then do;
rc=metadata_getattr(prop_uri,"DefaultValue",job_notify);
end;
else if prop_name = "JobDefaultEmailNotificationAddress" then do;
rc=metadata_getattr(prop_uri,"DefaultValue",job_address);
end;
end;
if flow_notify = "No" then flow_notify="Disabled";
if flow_notify = "0" then flow_notify="Start Only";
if flow_notify = "1" then flow_notify="Start and End";
if flow_notify = "2" then flow_notify="Start and End with Error";
if flow_notify = "3" then flow_notify="End with Error";
if flow_notify = "4" then flow_notify="End Only";
if job_notify = "No" then job_notify="Disabled";
if job_notify = "0" then job_notify="Start Only";
if job_notify = "1" then job_notify="Start and End";
if job_notify = "4" then job_notify="End Only";
output;
end;
run;
This sample code builds a number of tables about flows, jobs and data step batch servers. The last data step pulls the flow email configuration.
/******************************************************************************/
/* This program creates a set of tables from Metadata containing information */
/* about jobs and flows. */
/* These tables are: */
/* Flows - a tables of defined flows */
/* Flowsched - a table of scheduled flows and their schedules */
/* Flowjobs - a table of flow/job associations */
/* Flownoti - a table of flow email notification settings */
/* Jobs - a table of deployed jobs and their associated data servers */
/* dsbs - a table of data step batch servers and their attributes */
/* cmd - a table of any customized commands for jobs. */
/* Date: 09DEC2025 */
/******************************************************************************/
/* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. */
/* SPDX-License-Identifier: Apache-2.0 */
/* Provide connection to Metadata */
%let mduser=sasadm@saspw;
%let mdpass=password;
%let mdserver=meta.demo.sas.com;
%let mdport=8561;
/* Specify Data Step Batch Server values */
/* 9.4 Values */
%let dsbslogprop=BatchServer.DataStep.Property.LogDir.xmlKey.txt;
%let dsbslogext=BatchServer.DataStep.Property.LogExt.xmlKey.txt;
%let dsbscmdline=BatchServer.DataStep.Property.CmdLine.xmlKey.txt;
/* Set Metadata connection options based on macro variables set above. */
options metaserver="&mdserver"
metaport=&mdport
metaprotocol='bridge'
metauser="&mduser"
metapass="&mdpass"
metarepository='Foundation'
metaconnect='NONE'
;
/* Pull properties for the Data Step Batch Server(s) */
data dsbs (keep=dsbs_id dsbsname dsbscmdline dsbslogpath dsbslogext);
length type id prop_uri dsbs_uri $ 50 prop_name $ 255 propval dsbsname dsbscmdline dsbslogpath dsbslogext $ 1024 dsbs_id $ 17;
call missing (of _character_);
dsbsobj="omsobj:ServerComponent?@PublicType='Server.DataStepBatch'";
dsbscount=metadata_resolve(dsbsobj,type,id);
if dsbscount ge 1 then do j=1 to dsbscount;
rc=metadata_getnobj(dsbsobj,j,dsbs_uri);
rc=metadata_getattr(dsbs_uri,"Id",dsbs_id);
rc=metadata_getattr(dsbs_uri,"Name",dsbsname);
propcount=metadata_getnasn(dsbs_uri,"Properties",1,prop_uri);
if propcount > 0 then do i=1 to propcount;
rc=metadata_getnasn(dsbs_uri,"Properties",i,prop_uri);
rc=metadata_getattr(prop_uri,"Name",prop_name);
rc=metadata_getattr(prop_uri,"DefaultValue",propval);
if prop_name="&dsbscmdline" then do;
dsbscmdline=propval;
end;
else if prop_name="&dsbslogprop" then do;
dsbslogpath=propval;
end;
else if prop_name="&dsbslogext" then do;
dsbslogext=propval;
end;
end;
output;
end;
run;
/* Create table of flow IDs and schedule details */
data flowsched (keep=flow_id event_condition schedule);
length type id flow_uri step_uri event_uri prop_uri prop_name $ 50 flow_name $ 255 event_condition prop_val schedule $ 1024 flow_id $ 17;
call missing(of _character_);
flow_obj="omsobj:JFJob?@PublicType='DeployedFlow'";
flowcount=metadata_resolve(flow_obj,type,id);
put "NOTE: Found " flowcount "flows.";
if flowcount ge 1 then do i=1 to flowcount;
rc=metadata_getnobj(flow_obj,i,flow_uri);
rc=metadata_getattr(flow_uri,"Id",flow_id);
rc=metadata_getnasn(flow_uri,"Steps",1,step_uri);
rc=metadata_getnasn(step_uri,"TriggeringEvents",1,event_uri);
rc=metadata_getattr(event_uri,"Condition",event_condition);
propcount=metadata_getnasn(event_uri,"Properties",1,prop_uri);
if propcount ge 1 then do l=1 to propcount;
rc=metadata_getnasn(event_uri,"Properties",l,prop_uri);
rc=metadata_getattr(prop_uri,"Name",prop_name);
if prop_name = "Definition" then do;
rc=metadata_getattr(prop_uri,"DefaultValue",prop_val);
schedule=trim(prop_val);
output;
end;
end;
end;
run;
/* Create table of flow IDs and Names */
data flows (keep=flow_id flow_name);
length type id flow_uri $ 50 flow_name $ 255 flow_id $ 17;
call missing(of _character_);
flow_obj="omsobj:JFJob?@PublicType='DeployedFlow'";
flowcount=metadata_resolve(flow_obj,type,id);
put "NOTE: Found " flowcount "flows.";
if flowcount ge 1 then do i=1 to flowcount;
rc=metadata_getnobj(flow_obj,i,flow_uri);
rc=metadata_getattr(flow_uri,"Name",flow_name);
rc=metadata_getattr(flow_uri,"Id",flow_id);
output;
end;
run;
/* Create table of job details. */
data jobs (keep=job_name dir_name file_name dsbs_id job_id);
length type id job_uri file_uri dir_uri dsbs_uri $ 50 job_name dir_name file_name $ 255 dsbs_id job_id $ 17;
call missing(of _character_);
jobobj="omsobj:JFJob?@PublicType='DeployedJob'";
jobcount=metadata_resolve(jobobj,type,id);
if jobcount ge 1 then do i=1 to jobcount;
rc=metadata_getnobj(jobobj,i,job_uri);
rc=metadata_getattr(job_uri,"Name",job_name);
rc=metadata_getattr(job_uri,"Id",job_id);
rc=metadata_getnasn(job_uri,"SourceCode",1,file_uri);
rc=metadata_getattr(file_uri,"FileName",file_name);
rc=metadata_getnasn(file_uri,"Directories",1,dir_uri);
rc=metadata_getattr(dir_uri,"DirectoryName",dir_name);
rc=metadata_getnasn(job_uri,"TargetSpecifications",1,dsbs_uri);
rc=metadata_getattr(dsbs_uri,"Id",dsbs_id);
output;
end;
run;
/* Create table of custom command lines and their associated job. */
data cmd (keep=job_id cmd_val);
length type id cmd_uri step_uri job_uri $ 50 cmd_val $ 1024 job_id $ 17;
call missing (of _character_);
cmdobj="omsobj:Property?Property[@Name='CmdLine'][AssociatedObject/TransformationStep/Transformations/JFJob]";
cmdcount=metadata_resolve(cmdobj,type,id);
if cmdcount ge 1 then do i=1 to cmdcount;
rc=metadata_getnobj(cmdobj,i,cmd_uri);
rc=metadata_getattr(cmd_uri,"DefaultValue",cmd_val);
rc=metadata_getnasn(cmd_uri,"AssociatedObject",1,step_uri);
rc=metadata_getnasn(step_uri,"Transformations",1,job_uri);
rc=metadata_getattr(job_uri,"Id",job_id);
output;
end;
run;
/* Get Flow/Job associations */
data flowjobs (keep=flow_id job_id);
length type id flow_uri act_uri step_uri job_uri $ 50 flow_name $ 255 flow_id job_id $ 17;
call missing (of _character_);
flowobj="omsobj:JFJob?@PublicType='DeployedFlow'";
flowcount=metadata_resolve(flowobj,type,id);
if flowcount ge 1 then do i=1 to flowcount;
rc=metadata_getnobj(flowobj,i,flow_uri);
rc=metadata_getattr(flow_uri,"Id",flow_id);
actcount=metadata_getnasn(flow_uri,"JobActivities",1,step_uri);
if actcount ge 1 then do j=1 to actcount;
rc=metadata_getnasn(flow_uri,"JobActivities",j,act_uri);
stepcount=metadata_getnasn(act_uri,"Steps",1,step_uri);
if stepcount ge 1 then do k=1 to stepcount;
rc=metadata_getnasn(act_uri,"Steps",k,step_uri);
jobcount=metadata_getnasn(step_uri,"Transformations",1,job_uri);
if jobcount ge 1 then do l=1 to jobcount;
rc=metadata_getnasn(step_uri,"Transformations",l,job_uri);
rc=metadata_getattr(job_uri,"Id",job_id);
output;
end;
end;
end;
end;
run;
/* Extract sysin and log values from custom command. This is currently limited to UNIX-like paths. */
data cmd (keep=job_id cmd_val log sys);
set cmd;
if _N_=1 then do;
retain logpatternID syspatternID;
logpattern='/-log[[:space:]]+\/[A-z0-9\/\.]+/';
syspattern='/-sysin[[:space:]]+\/[A-z0-9\/\.]+/';
logpatternID=prxparse(logpattern);
syspatternID=prxparse(syspattern);
end;
call prxsubstr(logpatternID,cmd_val,position,length);
log=substr(cmd_val,position+5,length-5);
call prxsubstr(syspatternID,cmd_val,position,length);
sys=substr(cmd_val,position+7,length-7);
run;
/* Create a table of flow IDs and their email configurations */
data flownoti (keep=flow_id flow_notify job_notify flow_address job_address);
length type id flow_uri flow_id prop_uri prop_name $ 50 flow_notify job_notify $25 flow_address job_address $ 255;
call missing(of _character_);
obj="omsobj:JFJob?@PublicType='DeployedFlow'";
flowcount=metadata_resolve(obj,type,id);
put "NOTE: Found " flowcount "flows.";
if flowcount ge 1 then do i=1 to flowcount;
rc=metadata_getnobj(obj,i,flow_uri);
put "NOTE: Processing flow " flow_uri;
rc=metadata_getattr(flow_uri,"Id",flow_id);
propcount=metadata_getnasn(flow_uri,"Properties",1,prop_uri);
if propcount ge 1 then do j=1 to propcount;
rc=metadata_getnasn(flow_uri,"Properties",j,prop_uri);
rc=metadata_getattr(prop_uri,"Name",prop_name);
if prop_name = "EmailNotification" then do;
rc=metadata_getattr(prop_uri,"DefaultValue",flow_notify);
end;
else if prop_name = "EmailNotificationAddress" then do;
rc=metadata_getattr(prop_uri,"DefaultValue",flow_address);
end;
else if prop_name = "JobDefaultEmailNotification" then do;
rc=metadata_getattr(prop_uri,"DefaultValue",job_notify);
end;
else if prop_name = "JobDefaultEmailNotificationAddress" then do;
rc=metadata_getattr(prop_uri,"DefaultValue",job_address);
end;
end;
if flow_notify = "No" then flow_notify="Disabled";
if flow_notify = "0" then flow_notify="Start Only";
if flow_notify = "1" then flow_notify="Start and End";
if flow_notify = "2" then flow_notify="Start and End with Error";
if flow_notify = "3" then flow_notify="End with Error";
if flow_notify = "4" then flow_notify="End Only";
if job_notify = "No" then job_notify="Disabled";
if job_notify = "0" then job_notify="Start Only";
if job_notify = "1" then job_notify="Start and End";
if job_notify = "4" then job_notify="End Only";
output;
end;
run;
The SAS Users Group for Administrators (SUGA) is open to all SAS administrators and architects who install, update, manage or maintain a SAS deployment.
Learn how to explore data assets, create new data discovery agents, schedule data discovery agents, and much more.
Find more tutorials on the SAS Users YouTube channel.