Custom RSS feeds, based on ?query
authorZdeněk Šustr <sustr4@cesnet.cz>
Tue, 11 Dec 2012 16:28:49 +0000 (16:28 +0000)
committerZdeněk Šustr <sustr4@cesnet.cz>
Tue, 11 Dec 2012 16:28:49 +0000 (16:28 +0000)
org.glite.lb.doc/src/LBUG-Tools.tex
org.glite.lb.server/src/lb_proto.c

index 5606a05..8c14bd4 100644 (file)
@@ -198,20 +198,31 @@ This feature is supported since \LBver{3.3}
 \end{tabularx}
 
 \subsection{Job state changes as an RSS feed}
-The \LB includes an RSS interface allowing users to keep trace of their jobs in a very simple way using an RSS reader. The parameters of the RSS feeds are predefined, so no configuration is required.
+\LB includes an RSS interface allowing users to keep track of their jobs in a very simple way using an RSS reader. There are pre-defined feeds, which require no additional configuration. As of \LBver{4.0}, custom feeds can also be set up with the use of any recognized job condition.
 
-The address of a feed is given by the URL of the \LB server and a \textit{/RSS:rss\_feed\_name} postfix, e.g.
+The address of a pre-defined feed is given by the URL of the \LB server and a \textit{/RSS:rss\_feed\_name} postfix, e.g.
 \begin{verbatim}
    https://pelargir.ics.muni.cz:9000/RSS:finished
 \end{verbatim}  
 
-There are currently 3 feeds implemented in LB:
+\LBver{2.x and 3.x} implemented three distingushed feeds:
 \begin{itemize}
  \item \textit{finished} for jobs in terminal states (Done/OK, Aborted and Canceled)
  \item \textit{running} for running jobs
  \item \textit{aborted} for aborted jobs
 \end{itemize}
 
+\LBver{4.0} adds two new pre-defined feeds:
+\begin{itemize}
+ \item \textit{runningVM} for running virtual machines
+ \item \textit{doneVM} for succesfully finished virtual machines
+\end{itemize}
+
+Besides that, \LBver{4.0} makes the \texttt{?query} query string (explained in Section \ref{HTML:queries}) available for defining custom RSS feeds. To set up a custom feed, do not specify a feed name but add the \texttt{?query} query string to your URL, after the \texttt{RSS:} prefix. For instance, to set up an RSS feed of jobs in either \emph{submitted} or \emph{scheduled} state:
+\begin{verbatim}
+   https://pelargir.ics.muni.cz:9000/RSS:?query=status=submitted|=scheduled
+\end{verbatim}  
+
 \subsection{Other useful tools}
 
 For debugging purposes, low-level commands for getting \LB job status and job related events are provided in 
index f0236ed..f2edda3 100644 (file)
@@ -344,127 +344,62 @@ static void freeNotifInfo(notifInfo *ni){
        if (ni->jobid) free(ni->jobid);
 }
 
-static int getJobsRSS(edg_wll_Context ctx, char *feedType, edg_wll_JobStat **statesOut){
+static int getJobsRSS(edg_wll_Context ctx, char *feedType, edg_wll_JobStat **statesOut, edg_wll_QueryRec **qconditions){
        edg_wlc_JobId *jobsOut;
         //edg_wll_JobStat *statesOut;
         edg_wll_QueryRec **conds;
        int i;
-       enum FEED_TYPE {
-               FINISHED,
-               RUNNING_VM,
-               DONE_VM,
-               RUNNING,
-               ABORTED} feed;
+       char *fullrssquery = NULL;
 
        char *can_peername = edg_wll_gss_normalize_subj(ctx->peerName, 0);
 
-       if (strcmp(feedType, "finished") == 0) { feed = FINISHED; }
-       else if ((strcmp(feedType, "runningVM") == 0)) { feed = RUNNING_VM; }
-       else if ((strcmp(feedType, "doneVM") == 0)) { feed = DONE_VM; }
-       else if (strcmp(feedType, "running") == 0) { feed = RUNNING; }
-       else if (strcmp(feedType, "aborted") == 0) { feed = ABORTED; }
-       else {  
-               *statesOut = NULL;
-               free(can_peername);
-               return -1;
-       }
+       if (strcmp(feedType, "finished") == 0) { asprintf(&fullrssquery, "owner=%s&status=done|=aborted|=cancelled", can_peername); }
+       else if ((strcmp(feedType, "runningVM") == 0)) { asprintf(&fullrssquery, "owner=%s&status=running&jobtype=virtual_machine", can_peername); }
+       else if ((strcmp(feedType, "doneVM") == 0)) { asprintf(&fullrssquery, "owner=%s&status=done&donecode=ok&jobtype=virtual_machine", can_peername); }
+       else if (strcmp(feedType, "running") == 0) { asprintf(&fullrssquery, "owner=%s&status=running", can_peername); }
+       else if (strcmp(feedType, "aborted") == 0) { asprintf(&fullrssquery, "owner=%s&status=aborted", can_peername); }
 
-       switch (feed) {
-          case FINISHED:
-          case RUNNING:
-          case ABORTED:
-               conds = malloc(4*sizeof(*conds));
-               conds[0] = malloc(2*sizeof(**conds));
-               conds[0][0].attr = EDG_WLL_QUERY_ATTR_OWNER;
-               conds[0][0].op = EDG_WLL_QUERY_OP_EQUAL;
-               conds[0][0].value.c = can_peername;
-               conds[0][1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
-               switch (feed) {
-                   case FINISHED:
-                       conds[1] = malloc(4*sizeof(**conds));
-                       conds[1][0].attr = EDG_WLL_QUERY_ATTR_STATUS;
-                       conds[1][0].op = EDG_WLL_QUERY_OP_EQUAL;
-                       conds[1][0].value.i = EDG_WLL_JOB_DONE;
-                       conds[1][1].attr = EDG_WLL_QUERY_ATTR_STATUS;
-                       conds[1][1].op = EDG_WLL_QUERY_OP_EQUAL;
-                       conds[1][1].value.i = EDG_WLL_JOB_ABORTED;
-                       conds[1][2].attr = EDG_WLL_QUERY_ATTR_STATUS;
-                       conds[1][2].op = EDG_WLL_QUERY_OP_EQUAL;
-                       conds[1][2].value.i = EDG_WLL_JOB_CANCELLED;
-                       conds[1][3].attr = EDG_WLL_QUERY_ATTR_UNDEF;
-                       break;
-                   case RUNNING:
-                       conds[1] = malloc(2*sizeof(**conds));
-                       conds[1][0].attr = EDG_WLL_QUERY_ATTR_STATUS;
-                       conds[1][0].op = EDG_WLL_QUERY_OP_EQUAL;
-                       conds[1][0].value.i = EDG_WLL_JOB_RUNNING;
-                       conds[1][1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
-                       break;
-                   case ABORTED:
-                       conds[1] = malloc(2*sizeof(**conds));
-                       conds[1][0].attr = EDG_WLL_QUERY_ATTR_STATUS;
-                       conds[1][0].op = EDG_WLL_QUERY_OP_EQUAL;
-                       conds[1][0].value.i = EDG_WLL_JOB_ABORTED;
-                       conds[1][1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
-                       break;
+       if (fullrssquery) {
+               i = edg_wll_ParseQueryConditions(ctx, fullrssquery, &conds);
+               free(fullrssquery);
+               if (i) return i;
+       }
+       else {  
+               if (!qconditions) {
+                       *statesOut = NULL;
+                       free(can_peername);
+                       edg_wll_SetError(ctx, EINVAL, "Invalid RSS feed specified");
+                       return EINVAL;
                }
-               conds[2] = malloc(2*sizeof(**conds));
-               conds[2][0].attr = EDG_WLL_QUERY_ATTR_LASTUPDATETIME;
-               conds[2][0].op = EDG_WLL_QUERY_OP_GREATER;
-               conds[2][0].value.t.tv_sec = time(NULL) - ctx->rssTime;
-               conds[2][0].value.t.tv_usec = 0;
-               conds[2][1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
-               conds[3] = NULL;
-               break;
-          case RUNNING_VM:
-          case DONE_VM:
-               switch (feed) {
-                   case RUNNING_VM:
-                       conds = malloc(4*sizeof(*conds));
-                       conds[0] = malloc(2*sizeof(**conds));
-                       conds[0][0].attr = EDG_WLL_QUERY_ATTR_STATUS;
-                       conds[0][0].op = EDG_WLL_QUERY_OP_EQUAL;
-                       conds[0][0].value.i = EDG_WLL_JOB_RUNNING;
-                       conds[0][1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
-                       conds[3] = NULL;
-                       break;
-                   case DONE_VM:
-                       conds = malloc(5*sizeof(*conds));
-                       conds[0] = malloc(2*sizeof(**conds));
-                       conds[0][0].attr = EDG_WLL_QUERY_ATTR_STATUS;
-                       conds[0][0].op = EDG_WLL_QUERY_OP_EQUAL;
-                       conds[0][0].value.i = EDG_WLL_JOB_DONE;
-                       conds[0][1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
-                       conds[3] = malloc(2*sizeof(**conds));
-                       conds[3][0].attr = EDG_WLL_QUERY_ATTR_DONECODE;
-                       conds[3][0].op = EDG_WLL_QUERY_OP_EQUAL;
-                       conds[3][0].value.i = EDG_WLL_STAT_OK;
-                       conds[3][1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
-                       conds[4] = NULL;
-                       break;
+               else {
+                       conds = qconditions;
                }
-                conds[1] = malloc(2*sizeof(**conds));
-                conds[1][0].attr = EDG_WLL_QUERY_ATTR_LASTUPDATETIME;
-                conds[1][0].op = EDG_WLL_QUERY_OP_GREATER;
-                conds[1][0].value.t.tv_sec = time(NULL) - ctx->rssTime;
-                conds[1][0].value.t.tv_usec = 0;
-                conds[1][1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
-                conds[2] = malloc(2*sizeof(**conds));
-                conds[2][0].attr = EDG_WLL_QUERY_ATTR_JOB_TYPE;
-                conds[2][0].op = EDG_WLL_QUERY_OP_EQUAL;
-                conds[2][0].value.i = EDG_WLL_STAT_VIRTUAL_MACHINE;
-                conds[2][1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
-               break;
        }
 
+       for ( i=0; conds[i]; i++ ); // Look up the trailing position in the array: the time condition will be added to it
+
+       glite_common_log(LOG_CATEGORY_LB_SERVER_REQUEST, LOG_PRIORITY_DEBUG, "RSS: adding time condition (No. %d) to query. rssTime = %ld", i, (long)ctx->rssTime);
+
+       conds = realloc(conds,(i+2)*sizeof(*conds)); // Extend array by 1
+       
+       conds[i] = malloc(2*sizeof(**conds));
+       conds[i][0].attr = EDG_WLL_QUERY_ATTR_LASTUPDATETIME;
+       conds[i][0].op = EDG_WLL_QUERY_OP_GREATER;
+       conds[i][0].value.t.tv_sec = time(NULL) - ctx->rssTime;
+       conds[i][0].value.t.tv_usec = 0;
+       conds[i][1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+       conds[i+1] = NULL;
+
 
        if (edg_wll_QueryJobsServer(ctx, (const edg_wll_QueryRec **)conds, 0, &jobsOut, statesOut)){
                *statesOut = NULL;
        }
 
-       for (i = 0; conds[i]; i++)
-               free(conds[i]);
-       free(conds);
+       if (!qconditions) {
+               for (i = 0; conds[i]; i++)
+                       free(conds[i]);
+               free(conds);
+       }
        free(can_peername);
 
        return 0;
@@ -851,7 +786,7 @@ edg_wll_ErrorCode edg_wll_Proto(edg_wll_Context ctx,
        char **response,char ***headersOut,char **bodyOut,
        int *httpErr)
 {
-       char *requestPTR = NULL, *message = NULL, *requestMeat = NULL, *querystr, *queryconds = NULL, *flagstr, *queryflags = NULL;
+       char *requestPTR = NULL, *message = NULL, *requestMeat = NULL, *querystr, *queryconds = NULL, *flagstr, *queryflags = NULL, *feedType = NULL;
        int     ret = HTTP_OK;
        int     html = outputHTML(headers);
        int     text = 0; //XXX: Plain text communication is special case of html here, hence when text=1, html=1 too
@@ -1075,22 +1010,24 @@ edg_wll_ErrorCode edg_wll_Proto(edg_wll_Context ctx,
        /*GET /RSS:[feed type] RSS feed */
                } else if (strncmp(requestPTR, "/RSS:", strlen("/RSS:")) == 0){
                        edg_wll_JobStat *states;
-                       char *feedType;
                        int i;
                        int idx;
 
                        feedType = strdup(requestPTR + strlen("/RSS:"));
                        feedType[strrchr(feedType, ' ')-feedType] = 0;
-                       if (getJobsRSS(ctx, feedType, &states) < 0){
-                               char *errmessage;
-                               ret = HTTP_NOTFOUND;
-                               asprintf(&errmessage, "Unknown RSS feed \"%s\"", feedType);
-                               edg_wll_SetError(ctx, ENOENT, errmessage);
-                               free(errmessage);
-                               free(feedType);
-                                goto err;
+                       switch(getJobsRSS(ctx, feedType, &states, (edg_wll_QueryRec **)job_conditions)) {
+                               case 0:
+                                       break;
+                               case EINVAL:
+                                       ret = HTTP_INVALID;
+                                       goto err;
+                               case ENOSYS:
+                                       ret = HTTP_NOTIMPL;
+                                       goto err;
+                               default:
+                                       return HTTP_BADREQ;
+                                       goto err;
                        }
-                       free(feedType);
 
                        // check if owner and lastupdatetime is indexed
                        idx = 0;
@@ -1667,6 +1604,7 @@ err:      asprintf(response,"HTTP/1.1 %d %s",ret,edg_wll_HTTPErrorMessage(ret));
        }
 
        free(queryconds);
+       free(feedType);
 
        if (requestPTR) free(requestPTR);
        if (requestMeat) free(requestMeat);