BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
sathya66
Barite | Level 11

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

 

sathya66_0-1765192210097.png

 

 

 


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;
1 ACCEPTED SOLUTION

Accepted Solutions
gwootton
SAS Super FREQ

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;
--
Greg Wootton | Principal Systems Technical Support Engineer

View solution in original post

3 REPLIES 3
gwootton
SAS Super FREQ
Email notifications are set at both the job and flow level, so you can send an email notification when:

Flows (driven by the "FlowStateMailWhen" value and "MailDestination" set for the flow)
- Flow Starts
- Flow Ends (normally or with errors)
- Flow Ends (with errors)
Jobs (driven by the MailWhenFinish and MailWhenStart booleans to a "MailDestination" set within each job)
- Job Starts
- Job Ends
- Job Starts and Ends

I'm not sure where this code came from, but as it is already parsing the dat files line by line, it could be modified to look within the JFFlow for the FlowStateMailWhen and MailDestination attributes (and the JFJobs for the MailWhenFinish/MailWhenStart and MailDestination attributes) and parse them too.

I think the MailWhenStart/MailWhenFinish are only present when the job notifications are enabled, and have a value of "Yes". For "FlowStateMailWhen", it seems the possible values are probably "None", "End", "Exit", "Start", "StartEnd" and "StartExit".

I would mention this information is also available from Metadata if the jobs were scheduled there, in the properties "EmailNotification", "EmailNotificationAddress", "JobDefaultEmailNotification" and "JobDefaultEmailNotificationAddress". There, the "(Job)EmailNotification" properties use a numeric code to correspond to the various possibilities:

EmailNotification property (flow):
No notification: "No"
On Start only: 0
On Start and End (any exit code): 1
On Start and End (only error): 2
On End only (only error): 3
On End only (any exit code): 4

JobDefaultEmailNotification property (Jobs):
No notification: "No"
On start only: 0
On start and end: 1
On end only: 4
--
Greg Wootton | Principal Systems Technical Support Engineer
sathya66
Barite | Level 11
is there any code that I can try to extract email notifications, flow settings, etc from metadata ?
gwootton
SAS Super FREQ

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;
--
Greg Wootton | Principal Systems Technical Support Engineer

suga badge.PNGThe SAS Users Group for Administrators (SUGA) is open to all SAS administrators and architects who install, update, manage or maintain a SAS deployment. 

Join SUGA 

Get Started with SAS Information Catalog in SAS Viya

SAS technical trainer Erin Winters shows you how to explore assets, create new data discovery agents, schedule data discovery agents, and much more.

Find more tutorials on the SAS Users YouTube channel.

Discussion stats
  • 3 replies
  • 173 views
  • 2 likes
  • 2 in conversation